Files
developtools_ace-js2bundle/ace-loader/src/resource-plugin.js
T
laibo e693ae0066 laibo2@huawei.com
Signed-off-by: laibo <laibo2@huawei.com>
2022-03-03 10:44:10 +08:00

400 lines
13 KiB
JavaScript

/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');
const CUSTOM_THEME_PROP_GROUPS = require('./theme/customThemeStyles');
const OHOS_THEME_PROP_GROUPS = require('./theme/ohosStyles');
import { mkDir } from './util';
const FILE_EXT_NAME = ['.js', '.css', '.jsx', '.less', '.sass', '.scss', '.md', '.DS_Store', '.hml', '.json'];
const red = '\u001b[31m';
const reset = '\u001b[39m';
let input = '';
let output = '';
let manifestFilePath = '';
let watchCSSFiles;
let shareThemePath = '';
let internalThemePath = '';
function copyFile(input, output) {
try {
if (fs.existsSync(input)) {
const parent = path.join(output, '..');
if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
mkDir(parent);
}
const pathInfo = path.parse(input);
const entryObj = addPageEntryObj();
const indexPath = pathInfo.dir + path.sep + pathInfo.name + '.hml?entry';
for (const key in entryObj) {
if (entryObj[key] === indexPath) {
return;
}
}
if (pathInfo.ext === '.json' && (pathInfo.dir === shareThemePath ||
pathInfo.dir === internalThemePath)) {
if (themeFileBuild(input, output)) {
return;
}
}
const readStream = fs.createReadStream(input);
const writeStream = fs.createWriteStream(output);
readStream.pipe(writeStream);
readStream.on('close', function() {
writeStream.end();
});
}
} catch (e) {
console.error(red, `Failed to build file ${input}.`, reset);
throw e.message;
}
}
function circularFile(inputPath, outputPath, ext) {
const realPath = path.join(inputPath, ext);
const localI18n = path.join(input, 'i18n');
if (!fs.existsSync(realPath) || realPath === output || realPath === localI18n) {
return;
}
fs.readdirSync(realPath).forEach(function(file_) {
const file = path.join(realPath, file_);
const fileStat = fs.statSync(file);
if (fileStat.isFile()) {
const baseName = path.basename(file);
const extName = path.extname(file);
if (FILE_EXT_NAME.indexOf(extName) < 0 && baseName !== '.DS_Store') {
const outputFile = path.join(outputPath, ext, path.basename(file_));
if (outputFile === path.join(output, 'manifest.json')) {
return;
}
if (fs.existsSync(outputFile)) {
const outputFileStat = fs.statSync(outputFile);
if (outputFileStat.isFile() && fileStat.size !== outputFileStat.size) {
copyFile(file, outputFile);
}
} else {
copyFile(file, outputFile);
}
}
} else if (fileStat.isDirectory()) {
circularFile(inputPath, outputPath, path.join(ext, file_));
}
});
}
class ResourcePlugin {
constructor(input_, output_, manifestFilePath_, watchCSSFiles_) {
input = input_;
output = output_;
manifestFilePath = manifestFilePath_;
watchCSSFiles = watchCSSFiles_;
shareThemePath = path.join(input_, '../share/resources/styles');
internalThemePath = path.join(input_, 'resources/styles');
}
apply(compiler) {
compiler.hooks.beforeCompile.tap('resource Copy', () => {
circularFile(input, output, '');
circularFile(input, output, '../share');
});
compiler.hooks.normalModuleFactory.tap('OtherEntryOptionPlugin', () => {
const cssFiles = readCSSInfo(watchCSSFiles);
if (process.env.DEVICE_LEVEL === 'card' && process.env.compileMode !== 'moduleJson') {
for(const entryKey in compiler.options.entry) {
setCSSEntry(cssFiles, entryKey);
}
writeCSSInfo(watchCSSFiles, cssFiles);
return;
}
addPageEntryObj();
entryObj = Object.assign(entryObj, abilityEntryObj);
for (const key in entryObj) {
if (!compiler.options.entry[key]) {
const singleEntry = new SingleEntryPlugin('', entryObj[key], key);
singleEntry.apply(compiler);
}
setCSSEntry(cssFiles, key);
}
writeCSSInfo(watchCSSFiles, cssFiles);
});
compiler.hooks.done.tap('copyManifest', () => {
copyManifest();
if (fs.existsSync(path.join(output, 'app.js'))) {
fs.utimesSync(path.join(output, 'app.js'), new Date(), new Date());
}
});
}
}
function copyManifest() {
copyFile(manifestFilePath, path.join(output, 'manifest.json'));
}
let entryObj = {};
function addPageEntryObj() {
entryObj = {};
if (process.env.abilityType === 'page') {
const jsonString = readManifest(manifestFilePath);
const pages = jsonString.pages;
if (pages === undefined) {
throw Error('ERROR: missing pages').message;
}
pages.forEach((element) => {
const sourcePath = element;
const hmlPath = path.join(input, sourcePath + '.hml');
const aceSuperVisualPath = process.env.aceSuperVisualPath || '';
const visualPath = path.join(aceSuperVisualPath, sourcePath + '.visual');
const isHml = fs.existsSync(hmlPath);
const isVisual = fs.existsSync(visualPath);
const projectPath = process.env.projectPath;
if (isHml && isVisual) {
console.error('ERROR: ' + sourcePath + ' cannot both have hml && visual');
} else if (isHml) {
entryObj['./' + element] = path.resolve(projectPath, './' + sourcePath + '.hml?entry');
} else if (isVisual) {
entryObj['./' + element] = path.resolve(aceSuperVisualPath, './' + sourcePath +
'.visual?entry');
}
});
}
if (process.env.isPreview !== 'true' && process.env.DEVICE_LEVEL === 'rich') {
loadWorker(entryObj);
}
return entryObj;
}
let abilityEntryObj = {};
function addAbilityEntryObj(moduleJson) {
abilityEntryObj = {};
const entranceFiles = readAbilityEntrance(moduleJson);
entranceFiles.forEach(filePath => {
const key = filePath.replace(/^\.\/js\//, './').replace(/\.js$/, '');
abilityEntryObj[key] = path.resolve(process.env.projectPath, '../', filePath);
});
return abilityEntryObj;
}
function readAbilityEntrance(moduleJson) {
const entranceFiles = [];
if (moduleJson.module) {
if (moduleJson.module.srcEntrance) {
entranceFiles.push(moduleJson.module.srcEntrance);
}
if (moduleJson.module.abilities && moduleJson.module.abilities.length) {
readEntrances(moduleJson.module.abilities, entranceFiles);
}
if (moduleJson.module.extensionAbilities && moduleJson.module.extensionAbilities.length) {
readEntrances(moduleJson.module.extensionAbilities, entranceFiles);
}
}
return entranceFiles;
}
function readEntrances(abilities, entranceFiles) {
abilities.forEach(ability => {
if ((!ability.type || ability.type !== 'form') && ability.srcEntrance) {
entranceFiles.push(ability.srcEntrance);
}
});
}
function readManifest(manifestFilePath) {
let manifest = {};
try {
if (fs.existsSync(manifestFilePath)) {
const jsonString = fs.readFileSync(manifestFilePath).toString();
manifest = JSON.parse(jsonString);
} else if (process.env.aceModuleJsonPath && fs.existsSync(process.env.aceModuleJsonPath)) {
buildManifest(manifest);
} else {
throw Error('\u001b[31m' + 'ERROR: the manifest.json or module.json is lost.' +
'\u001b[39m').message;
}
} catch (e) {
throw Error('\u001b[31m' + 'ERROR: the manifest.json or module.json file format is invalid.' +
'\u001b[39m').message;
}
return manifest;
}
function readModulePages(moduleJson) {
if (moduleJson.module.uiSyntax === 'hml' && moduleJson.module.pages) {
const modulePagePath = path.resolve(process.env.aceProfilePath,
`${moduleJson.module.pages.replace(/\$profile\:/, '')}.json`);
if (fs.existsSync(modulePagePath)) {
const pagesConfig = JSON.parse(fs.readFileSync(modulePagePath, 'utf-8'));
return pagesConfig.src;
}
}
}
function readFormPages(moduleJson) {
const pages = [];
if (moduleJson.module.extensionAbilities && moduleJson.module.extensionAbilities.length) {
moduleJson.module.extensionAbilities.forEach(extensionAbility => {
if (extensionAbility.type && extensionAbility.type === 'form' && extensionAbility.metadata &&
extensionAbility.metadata.length) {
extensionAbility.metadata.forEach(item => {
if (item.resource && /\$profile\:/.test(item.resource)) {
parseFormConfig(item.resource, pages);
}
});
}
});
}
return pages;
}
function parseFormConfig(resource, pages) {
const resourceFile = path.resolve(process.env.aceProfilePath,
`${resource.replace(/\$profile\:/, '')}.json`);
if (fs.existsSync(resourceFile)) {
const pagesConfig = JSON.parse(fs.readFileSync(resourceFile, 'utf-8'));
if (pagesConfig.forms && pagesConfig.forms.length) {
pagesConfig.forms.forEach(form => {
if (form.src) {
pages.push(form.src);
}
});
}
}
}
function buildManifest(manifest) {
try {
const moduleJson = JSON.parse(fs.readFileSync(process.env.aceModuleJsonPath).toString());
if (process.env.DEVICE_LEVEL === 'rich') {
manifest.pages = readModulePages(moduleJson);
manifest.abilityEntryObj = addAbilityEntryObj(moduleJson);
}
if (process.env.DEVICE_LEVEL === 'card') {
manifest.pages = readFormPages(moduleJson);
}
manifest.minPlatformVersion = moduleJson.app.minAPIVersion;
} catch (e) {
throw Error("\x1B[31m" + 'ERROR: the module.json file is lost or format is invalid.' +
"\x1B[39m").message;
}
}
function loadWorker(entryObj) {
if(validateWorkOption()) {
const workerConfig = JSON.parse(fs.readFileSync(process.env.aceWorkPath).toString());
workerConfig.forEach(worker => {
const relativePath = path.ralative(process.env.projectPath, worker);
if (filterWorker(relativePath)) {
entryObj[relativePath.replace(/\.(ts|js)$/,'')] = worker;
}
})
} else {
const workerPath = path.resolve(input, 'workers');
if (fs.existsSync(workerPath)) {
const workerFiles = [];
readFile(workerPath, workerFiles);
workerFiles.forEach((item) => {
if (/\.js$/.test(item)) {
const relativePath = path.relative(workerPath, item).replace(/\.js$/, '');
entryObj[`./workers/` + relativePath] = item;
}
});
}
}
}
function validateWorkOption() {
if (process.env.aceWorkPath && fs.existsSync(process.env.aceWorkPath)) {
const workerConfig = JSON.parse(fs.readFileSync(process.env.aceWorkPath).toString());
if(workerConfig.workers) {
return true;
}
}
return false;
}
function filterWorker(workerPath) {
return /\.(ts|js)$/.test(workerPath) && !/^\.\./.test(workerPath);
}
function readFile(dir, utFiles) {
try {
const files = fs.readdirSync(dir);
files.forEach((element) => {
const filePath = path.join(dir, element);
const status = fs.statSync(filePath);
if (status.isDirectory()) {
readFile(filePath, utFiles);
} else {
utFiles.push(filePath);
}
});
} catch (e) {
console.error(e.message);
}
}
function themeFileBuild(customThemePath, customThemeBuild) {
if (fs.existsSync(customThemePath)) {
const themeContent = JSON.parse(fs.readFileSync(customThemePath));
const newContent = {};
if (themeContent && themeContent['style']) {
newContent['style'] = {};
const styleContent = themeContent['style'];
Object.keys(styleContent).forEach(function(key) {
const customKey = CUSTOM_THEME_PROP_GROUPS[key];
const ohosKey = OHOS_THEME_PROP_GROUPS[key];
if (ohosKey) {
newContent['style'][ohosKey] = styleContent[key];
} else if (customKey) {
newContent['style'][customKey] = styleContent[key];
} else {
newContent['style'][key] = styleContent[key];
}
});
fs.writeFileSync(customThemeBuild, JSON.stringify(newContent, null, 2));
return true;
}
}
return false;
}
function readCSSInfo(watchCSSFiles) {
if (fs.existsSync(watchCSSFiles)) {
return JSON.parse(fs.readFileSync(watchCSSFiles));
} else {
return {};
}
}
function writeCSSInfo(filePath, infoObject) {
if (!fs.existsSync(path.resolve(filePath, '..'))) {
mkDir(path.resolve(filePath, '..'));
}
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
fs.writeFileSync(filePath, JSON.stringify(infoObject, null, 2));
}
function setCSSEntry(cssfiles, key) {
cssfiles["entry"] = cssfiles["entry"] || {};
if (fs.existsSync(path.join(input, key + '.css'))) {
cssfiles["entry"][path.join(input, key + '.css')] = true;
}
}
module.exports = ResourcePlugin;