@codelet/cli-service
Version:
cli-service
265 lines (264 loc) • 11 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createOptimization = exports.RewriteExternalRequestPlugin = exports.createWatchIgnored = exports.createNpmDirRequestResolver = exports.createExternalRequestResolver = exports.createNpmDirCopyPatterns = exports.createExternalCopyPatterns = exports.resolveExternalFiles = exports.parseDir = exports.resolveSourceRoot = exports.resolveEntryRoot = exports.resolve = exports.externalRequestPlaceholderPrefix = exports.cmd = void 0;
exports.getOptionValue = getOptionValue;
exports.parseArgv = parseArgv;
exports.getConfig = getConfig;
const fast_glob_1 = __importDefault(require("fast-glob"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const webpack_1 = require("webpack");
const webpack_merge_1 = __importDefault(require("webpack-merge"));
const config_1 = require("./config");
exports.cmd = process.cwd();
exports.externalRequestPlaceholderPrefix = '__CODELET_EXTERNAL__/';
const resolve = (...args) => path_1.default.resolve(exports.cmd, ...args);
exports.resolve = resolve;
const resolveEntryRoot = (entryPath) => (0, exports.resolve)(entryPath);
exports.resolveEntryRoot = resolveEntryRoot;
const resolveSourceRoot = (entryPath) => (0, exports.resolveEntryRoot)(entryPath);
exports.resolveSourceRoot = resolveSourceRoot;
const parseDir = (entryPath, source) => {
entryPath = (0, exports.resolveEntryRoot)(entryPath);
const filepaths = fast_glob_1.default.sync(source.map((item) => (0, exports.resolve)(entryPath, item).replace(/\\/g, '/')));
const entry = filepaths.reduce((res, filepath) => {
const relPath = path_1.default.relative(entryPath, filepath);
const { dir, name } = path_1.default.parse(relPath);
res[path_1.default.join(dir, name)] = {
import: filepath,
runtime: 'runtime',
};
return res;
}, {});
return { entry };
};
exports.parseDir = parseDir;
const resolveExternalFiles = (entryPath, externalSource) => {
const entryRoot = (0, exports.resolveEntryRoot)(entryPath);
return fast_glob_1.default
.sync(externalSource.map((item) => path_1.default.join(entryRoot, item).replace(/\\/g, '/')))
.map((filepath) => path_1.default.resolve(filepath));
};
exports.resolveExternalFiles = resolveExternalFiles;
const createExternalCopyPatterns = (entryPath, externalSource) => {
const entryRoot = (0, exports.resolveEntryRoot)(entryPath);
return externalSource.map((pattern) => ({
context: entryRoot,
from: pattern,
to: '[path][name][ext]',
noErrorOnMissing: true,
info: {
minimized: true,
},
}));
};
exports.createExternalCopyPatterns = createExternalCopyPatterns;
const createNpmDirCopyPatterns = (npmDir) => {
if (!npmDir) {
return [];
}
return [
{
from: (0, exports.resolve)(npmDir),
to: path_1.default.basename(npmDir),
noErrorOnMissing: true,
info: {
minimized: true,
},
},
];
};
exports.createNpmDirCopyPatterns = createNpmDirCopyPatterns;
const createExternalRequestResolver = (options) => {
const entryRoot = (0, exports.resolveEntryRoot)(options.entryPath);
const externalFileSet = new Set(options.externalFiles);
return (context, request) => {
const candidates = [];
// 同时支持 "@/libs/foo" 这类别名引用和 "./libs/foo" 这类相对引用。
if (request.startsWith('@/')) {
candidates.push(path_1.default.join(entryRoot, request.slice(2)));
}
else if (request.startsWith('.')) {
candidates.push(path_1.default.resolve(context, request));
}
else {
return '';
}
for (const basePath of candidates) {
for (const ext of ['', '.js']) {
const candidate = ext ? `${basePath}${ext}` : basePath;
if (externalFileSet.has(path_1.default.resolve(candidate))) {
return path_1.default.resolve(candidate);
}
}
}
return '';
};
};
exports.createExternalRequestResolver = createExternalRequestResolver;
const createNpmDirRequestResolver = (npmDir) => {
const npmRoot = (0, exports.resolve)(npmDir);
return (request) => {
if (!request ||
request.startsWith('.') ||
request.startsWith('@/') ||
path_1.default.isAbsolute(request)) {
return '';
}
const requestPath = request.replace(/\\/g, '/');
const candidates = [
path_1.default.join(npmRoot, requestPath),
path_1.default.join(npmRoot, `${requestPath}.js`),
path_1.default.join(npmRoot, `${requestPath}.json`),
path_1.default.join(npmRoot, requestPath, 'index.js'),
path_1.default.join(npmRoot, requestPath, 'index.json'),
];
return candidates.some((candidate) => fs_1.default.existsSync(candidate)) ? request : '';
};
};
exports.createNpmDirRequestResolver = createNpmDirRequestResolver;
const createWatchIgnored = (dirs) => {
return dirs.filter(Boolean).map((dir) => (0, exports.resolve)(dir).replace(/\\/g, '/'));
};
exports.createWatchIgnored = createWatchIgnored;
class RewriteExternalRequestPlugin {
apply(compiler) {
compiler.hooks.thisCompilation.tap('RewriteExternalRequestPlugin', (compilation) => {
compilation.hooks.processAssets.tap({
name: 'RewriteExternalRequestPlugin',
stage: webpack_1.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_COMPATIBILITY,
}, (assets) => {
for (const assetName of Object.keys(assets)) {
if (!assetName.endsWith('.js')) {
continue;
}
const source = assets[assetName].source().toString();
if (!source.includes(exports.externalRequestPlaceholderPrefix)) {
continue;
}
// 基于最终产物文件的位置重新计算外部 require 路径,
// 这样像 bundle.js 这类公共 chunk 也能拿到正确的运行时相对路径。
const rewritten = source.replace(/__CODELET_EXTERNAL__\/([^"'`]+)/g, (_match, target) => {
return path_1.default
.relative(path_1.default.dirname(assetName), target)
.replace(/\\/g, '/')
.replace(/^(?!\.)/, './');
});
assets[assetName] = new webpack_1.sources.RawSource(rewritten);
}
});
});
}
}
exports.RewriteExternalRequestPlugin = RewriteExternalRequestPlugin;
const createOptimization = () => {
const packageRootPattern = /[\\/]src[\\/]packages[\\/]([^\\/]+)[\\/]/;
const getPackageBundleName = (name) => {
const normalizedName = name.replace(/\\/g, '/');
if (normalizedName.startsWith('packages/')) {
const [, packageName] = normalizedName.split('/');
if (packageName) {
return `packages/${packageName}/bundle`;
}
}
return null;
};
const getModuleResource = (module) => {
if (!module || typeof module !== 'object') {
return '';
}
return 'resource' in module && typeof module.resource === 'string' ? module.resource : '';
};
return {
splitChunks: {
chunks: 'all',
minChunks: 2,
minSize: 0,
cacheGroups: {
subpackage: {
test(module) {
return Boolean(getModuleResource(module).match(packageRootPattern));
},
name(module, chunks) {
const match = getModuleResource(module).match(packageRootPattern);
if (!match) {
return 'bundle';
}
const packageBundleName = `packages/${match[1]}/bundle`;
const bundleNames = new Set(chunks
.map((chunk) => chunk.name)
.filter((name) => typeof name === 'string' && Boolean(name))
.map((name) => getPackageBundleName(name))
.filter((name) => typeof name === 'string' && Boolean(name)));
return bundleNames.size <= 1 && bundleNames.has(packageBundleName)
? packageBundleName
: 'bundle';
},
priority: 10,
minChunks: 2,
chunks: 'all',
},
main: {
name: 'bundle',
minChunks: 2,
chunks: 'all',
},
},
},
};
};
exports.createOptimization = createOptimization;
function getOptionValue(argv, option) {
const index = argv.findIndex((item) => item === option);
if (index > -1) {
return argv[index + 1];
}
}
function parseArgv(argv) {
const isWatch = argv.includes('--watch');
const isDev = argv.includes('dev');
const pageIndex = getOptionValue(argv, '--pageIndex') || '';
const configRelativePath = getOptionValue(argv, '--config') || 'codelet.config.js';
const configPath = (0, exports.resolve)(configRelativePath);
return { configPath, isWatch, pageIndex, isDev };
}
function getConfig(options) {
const { configPath, isWatch } = options;
let config;
if (fs_1.default.existsSync(configPath)) {
const cfg = require(configPath);
if (typeof cfg === 'function') {
config = (0, config_1.getDefaultConfig)(options);
config = cfg(config);
}
else {
if (options.pageIndex) {
cfg.pageIndex = options.pageIndex;
}
if (options.isDev) {
cfg.isDev = options.isDev;
}
config = (0, config_1.getDefaultConfig)(cfg);
config = {
...config,
...cfg,
...(0, webpack_merge_1.default)(config.webpack ?? {}, cfg.webpack ?? {}),
};
}
}
else {
config = (0, config_1.getDefaultConfig)(options);
}
const { entryPath, source, webpack } = config;
if (!webpack.entry) {
const { entry } = (0, exports.parseDir)(entryPath, source);
webpack.entry = entry;
}
if (isWatch) {
webpack.watch = true;
}
return webpack;
}