407 lines
15 KiB
JavaScript
407 lines
15 KiB
JavaScript
"use strict";
|
||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||
};
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.transformDynamicImports = exports.parseScriptDescriptor = exports.parseTemplateDescriptor = exports.updateMiniProgramComponentsByMainFilename = exports.updateMiniProgramGlobalComponents = exports.updateMiniProgramComponentsByTemplateFilename = exports.updateMiniProgramComponentsByScriptFilename = exports.parseMainDescriptor = void 0;
|
||
const types_1 = require("@babel/types");
|
||
const estree_walker_1 = require("estree-walker");
|
||
const magic_string_1 = __importDefault(require("magic-string"));
|
||
const shared_1 = require("@vue/shared");
|
||
const uni_shared_1 = require("@dcloudio/uni-shared");
|
||
const messages_1 = require("../messages");
|
||
const constants_1 = require("../constants");
|
||
const utils_1 = require("../utils");
|
||
const utils_2 = require("../vite/utils");
|
||
const jsonFile_1 = require("../json/mp/jsonFile");
|
||
const mainDescriptors = new Map();
|
||
const scriptDescriptors = new Map();
|
||
const templateDescriptors = new Map();
|
||
function findImportTemplateSource(ast) {
|
||
const importDeclaration = ast.body.find((node) => (0, types_1.isImportDeclaration)(node) &&
|
||
node.source.value.includes('vue&type=template'));
|
||
if (importDeclaration) {
|
||
return importDeclaration.source.value;
|
||
}
|
||
}
|
||
function findImportScriptSource(ast) {
|
||
const importDeclaration = ast.body.find((node) => (0, types_1.isImportDeclaration)(node) && node.source.value.includes('vue&type=script'));
|
||
if (importDeclaration) {
|
||
return importDeclaration.source.value;
|
||
}
|
||
}
|
||
async function resolveSource(filename, source, resolve) {
|
||
if (!source) {
|
||
return;
|
||
}
|
||
const resolveId = await resolve(source, filename);
|
||
if (resolveId) {
|
||
return resolveId.id;
|
||
}
|
||
}
|
||
async function parseMainDescriptor(filename, ast, resolve) {
|
||
const script = await resolveSource(filename, findImportScriptSource(ast), resolve);
|
||
const template = await resolveSource(filename, findImportTemplateSource(ast), resolve);
|
||
const imports = await parseVueComponentImports(filename, ast.body.filter((node) => (0, types_1.isImportDeclaration)(node)), resolve);
|
||
if (!script) {
|
||
// inline script
|
||
await parseScriptDescriptor(filename, ast, { resolve, isExternal: false });
|
||
}
|
||
if (!template) {
|
||
// inline template
|
||
await parseTemplateDescriptor(filename, ast, { resolve, isExternal: false });
|
||
}
|
||
const descriptor = {
|
||
imports,
|
||
script: script ? (0, utils_2.parseVueRequest)(script).filename : filename,
|
||
template: template ? (0, utils_2.parseVueRequest)(template).filename : filename,
|
||
};
|
||
mainDescriptors.set(filename, descriptor);
|
||
return descriptor;
|
||
}
|
||
exports.parseMainDescriptor = parseMainDescriptor;
|
||
function updateMiniProgramComponentsByScriptFilename(scriptFilename, inputDir, normalizeComponentName) {
|
||
const mainFilename = findMainFilenameByScriptFilename(scriptFilename);
|
||
if (mainFilename) {
|
||
updateMiniProgramComponentsByMainFilename(mainFilename, inputDir, normalizeComponentName);
|
||
}
|
||
}
|
||
exports.updateMiniProgramComponentsByScriptFilename = updateMiniProgramComponentsByScriptFilename;
|
||
function updateMiniProgramComponentsByTemplateFilename(templateFilename, inputDir, normalizeComponentName) {
|
||
const mainFilename = findMainFilenameByTemplateFilename(templateFilename);
|
||
if (mainFilename) {
|
||
updateMiniProgramComponentsByMainFilename(mainFilename, inputDir, normalizeComponentName);
|
||
}
|
||
}
|
||
exports.updateMiniProgramComponentsByTemplateFilename = updateMiniProgramComponentsByTemplateFilename;
|
||
function findMainFilenameByScriptFilename(scriptFilename) {
|
||
const keys = [...mainDescriptors.keys()];
|
||
return keys.find((key) => mainDescriptors.get(key).script === scriptFilename);
|
||
}
|
||
function findMainFilenameByTemplateFilename(templateFilename) {
|
||
const keys = [...mainDescriptors.keys()];
|
||
return keys.find((key) => mainDescriptors.get(key).template === templateFilename);
|
||
}
|
||
async function updateMiniProgramGlobalComponents(filename, ast, { inputDir, resolve, normalizeComponentName, }) {
|
||
const { bindingComponents, imports } = await parseGlobalDescriptor(filename, ast, resolve);
|
||
(0, jsonFile_1.addMiniProgramUsingComponents)('app', createUsingComponents(bindingComponents, imports, inputDir, normalizeComponentName));
|
||
return {
|
||
imports,
|
||
};
|
||
}
|
||
exports.updateMiniProgramGlobalComponents = updateMiniProgramGlobalComponents;
|
||
function createUsingComponents(bindingComponents, imports, inputDir, normalizeComponentName) {
|
||
const usingComponents = {};
|
||
imports.forEach(({ source: { value }, specifiers: [specifier] }) => {
|
||
const { name } = specifier.local;
|
||
if (!bindingComponents[name]) {
|
||
return;
|
||
}
|
||
const componentName = normalizeComponentName((0, shared_1.hyphenate)(bindingComponents[name].tag));
|
||
if (!usingComponents[componentName]) {
|
||
usingComponents[componentName] = (0, uni_shared_1.addLeadingSlash)((0, utils_1.removeExt)((0, utils_1.normalizeMiniProgramFilename)(value, inputDir)));
|
||
}
|
||
});
|
||
return usingComponents;
|
||
}
|
||
function updateMiniProgramComponentsByMainFilename(mainFilename, inputDir, normalizeComponentName) {
|
||
const mainDescriptor = mainDescriptors.get(mainFilename);
|
||
if (!mainDescriptor) {
|
||
return;
|
||
}
|
||
const templateDescriptor = templateDescriptors.get(mainDescriptor.template);
|
||
if (!templateDescriptor) {
|
||
return;
|
||
}
|
||
const scriptDescriptor = scriptDescriptors.get(mainDescriptor.script);
|
||
if (!scriptDescriptor) {
|
||
return;
|
||
}
|
||
const bindingComponents = parseBindingComponents({
|
||
...templateDescriptor.bindingComponents,
|
||
...scriptDescriptor.setupBindingComponents,
|
||
}, scriptDescriptor.bindingComponents);
|
||
const imports = parseImports(mainDescriptor.imports, scriptDescriptor.imports, templateDescriptor.imports);
|
||
(0, jsonFile_1.addMiniProgramUsingComponents)((0, utils_1.removeExt)((0, utils_1.normalizeMiniProgramFilename)(mainFilename, inputDir)), createUsingComponents(bindingComponents, imports, inputDir, normalizeComponentName));
|
||
}
|
||
exports.updateMiniProgramComponentsByMainFilename = updateMiniProgramComponentsByMainFilename;
|
||
function findBindingComponent(tag, bindingComponents) {
|
||
return Object.keys(bindingComponents).find((name) => {
|
||
const componentTag = bindingComponents[name].tag;
|
||
const camelName = (0, shared_1.camelize)(componentTag);
|
||
const PascalName = (0, shared_1.capitalize)(camelName);
|
||
return tag === componentTag || tag === camelName || tag === PascalName;
|
||
});
|
||
}
|
||
function normalizeComponentId(id) {
|
||
// _unref(test) => test
|
||
if (id.includes('_unref(')) {
|
||
return id.replace('_unref(', '').replace(')', '');
|
||
}
|
||
// $setup["test"] => test
|
||
if (id.includes('$setup[')) {
|
||
return id.replace('$setup["', '').replace('"', '');
|
||
}
|
||
return id;
|
||
}
|
||
function parseBindingComponents(templateBindingComponents, scriptBindingComponents) {
|
||
const bindingComponents = {};
|
||
Object.keys(templateBindingComponents).forEach((id) => {
|
||
bindingComponents[normalizeComponentId(id)] = templateBindingComponents[id];
|
||
});
|
||
Object.keys(scriptBindingComponents).forEach((id) => {
|
||
const { tag } = scriptBindingComponents[id];
|
||
const name = findBindingComponent(tag, templateBindingComponents);
|
||
if (name) {
|
||
bindingComponents[id] = bindingComponents[name];
|
||
}
|
||
});
|
||
return bindingComponents;
|
||
}
|
||
function parseImports(mainImports, scriptImports, templateImports) {
|
||
const imports = [...mainImports, ...templateImports, ...scriptImports];
|
||
return imports;
|
||
}
|
||
/**
|
||
* 解析 template
|
||
* @param filename
|
||
* @param code
|
||
* @param ast
|
||
* @param options
|
||
* @returns
|
||
*/
|
||
async function parseTemplateDescriptor(filename, ast, options) {
|
||
// 外置时查找所有 vue component import
|
||
const imports = options.isExternal
|
||
? await parseVueComponentImports(filename, ast.body.filter((node) => (0, types_1.isImportDeclaration)(node)), options.resolve)
|
||
: [];
|
||
const descriptor = {
|
||
bindingComponents: findBindingComponents(ast.body),
|
||
imports,
|
||
};
|
||
templateDescriptors.set(filename, descriptor);
|
||
return descriptor;
|
||
}
|
||
exports.parseTemplateDescriptor = parseTemplateDescriptor;
|
||
async function parseGlobalDescriptor(filename, ast, resolve) {
|
||
// 外置时查找所有 vue component import
|
||
const imports = (await parseVueComponentImports(filename, ast.body.filter((node) => (0, types_1.isImportDeclaration)(node)), resolve)).filter((item) => !(0, utils_1.isAppVue)((0, utils_2.cleanUrl)(item.source.value)));
|
||
return {
|
||
bindingComponents: parseGlobalComponents(ast),
|
||
imports,
|
||
};
|
||
}
|
||
/**
|
||
* 解析 script
|
||
* @param filename
|
||
* @param code
|
||
* @param ast
|
||
* @param options
|
||
* @returns
|
||
*/
|
||
async function parseScriptDescriptor(filename, ast, options) {
|
||
// 外置时查找所有 vue component import
|
||
const imports = options.isExternal
|
||
? await parseVueComponentImports(filename, ast.body.filter((node) => (0, types_1.isImportDeclaration)(node)), options.resolve)
|
||
: [];
|
||
const descriptor = {
|
||
bindingComponents: parseComponents(ast),
|
||
setupBindingComponents: findBindingComponents(ast.body),
|
||
imports,
|
||
};
|
||
scriptDescriptors.set(filename, descriptor);
|
||
return descriptor;
|
||
}
|
||
exports.parseScriptDescriptor = parseScriptDescriptor;
|
||
/**
|
||
* 解析编译器生成的 bindingComponents
|
||
* @param ast
|
||
* @returns
|
||
*/
|
||
function findBindingComponents(ast) {
|
||
const mapping = findUnpluginComponents(ast);
|
||
for (const node of ast) {
|
||
if (!(0, types_1.isVariableDeclaration)(node)) {
|
||
continue;
|
||
}
|
||
const declarator = node.declarations[0];
|
||
if ((0, types_1.isIdentifier)(declarator.id) &&
|
||
declarator.id.name === constants_1.BINDING_COMPONENTS) {
|
||
const bindingComponents = JSON.parse(declarator.init.value);
|
||
return Object.keys(bindingComponents).reduce((bindings, tag) => {
|
||
const { name, type } = bindingComponents[tag];
|
||
bindings[mapping[name] || name] = {
|
||
tag,
|
||
type: type,
|
||
};
|
||
return bindings;
|
||
}, {});
|
||
}
|
||
}
|
||
return {};
|
||
}
|
||
/**
|
||
* 兼容:unplugin_components
|
||
* https://github.com/dcloudio/uni-app/issues/3057
|
||
* @param ast
|
||
* @returns
|
||
*/
|
||
function findUnpluginComponents(ast) {
|
||
const res = Object.create(null);
|
||
// if(!Array){}
|
||
const ifStatement = ast.find((statement) => (0, types_1.isIfStatement)(statement) &&
|
||
(0, types_1.isUnaryExpression)(statement.test) &&
|
||
statement.test.operator === '!' &&
|
||
(0, types_1.isIdentifier)(statement.test.argument) &&
|
||
statement.test.argument.name === 'Array');
|
||
if (!ifStatement) {
|
||
return res;
|
||
}
|
||
if (!(0, types_1.isBlockStatement)(ifStatement.consequent)) {
|
||
return res;
|
||
}
|
||
for (const node of ifStatement.consequent.body) {
|
||
if (!(0, types_1.isVariableDeclaration)(node)) {
|
||
continue;
|
||
}
|
||
const { id, init } = node.declarations[0];
|
||
if ((0, types_1.isIdentifier)(id) &&
|
||
(0, types_1.isIdentifier)(init) &&
|
||
init.name.includes('unplugin_components')) {
|
||
res[id.name] = init.name;
|
||
}
|
||
}
|
||
return res;
|
||
}
|
||
/**
|
||
* 查找全局组件定义:app.component('component-a',{})
|
||
* @param ast
|
||
* @returns
|
||
*/
|
||
function parseGlobalComponents(ast) {
|
||
const bindingComponents = {};
|
||
estree_walker_1.walk(ast, {
|
||
enter(child) {
|
||
if (!(0, types_1.isCallExpression)(child)) {
|
||
return;
|
||
}
|
||
const { callee } = child;
|
||
// .component
|
||
if (!(0, types_1.isMemberExpression)(callee) ||
|
||
!(0, types_1.isIdentifier)(callee.property) ||
|
||
callee.property.name !== 'component') {
|
||
return;
|
||
}
|
||
// .component('component-a',{})
|
||
const args = child.arguments;
|
||
if (args.length !== 2) {
|
||
return;
|
||
}
|
||
const [name, value] = args;
|
||
if (!(0, types_1.isStringLiteral)(name)) {
|
||
return console.warn(messages_1.M['mp.component.args[0]']);
|
||
}
|
||
if (!(0, types_1.isIdentifier)(value)) {
|
||
return console.warn(messages_1.M['mp.component.args[1]']);
|
||
}
|
||
bindingComponents[value.name] = {
|
||
tag: name.value,
|
||
type: 'unknown',
|
||
};
|
||
},
|
||
});
|
||
return bindingComponents;
|
||
}
|
||
/**
|
||
* 从 components 中查找定义的组件
|
||
* @param ast
|
||
* @param bindingComponents
|
||
*/
|
||
function parseComponents(ast) {
|
||
const bindingComponents = {};
|
||
estree_walker_1.walk(ast, {
|
||
enter(child) {
|
||
if (!(0, types_1.isObjectExpression)(child)) {
|
||
return;
|
||
}
|
||
const componentsProp = child.properties.find((prop) => (0, types_1.isObjectProperty)(prop) &&
|
||
(0, types_1.isIdentifier)(prop.key) &&
|
||
prop.key.name === 'components');
|
||
if (!componentsProp) {
|
||
return;
|
||
}
|
||
const componentsExpr = componentsProp.value;
|
||
if (!(0, types_1.isObjectExpression)(componentsExpr)) {
|
||
return;
|
||
}
|
||
componentsExpr.properties.forEach((prop) => {
|
||
if (!(0, types_1.isObjectProperty)(prop)) {
|
||
return;
|
||
}
|
||
if (!(0, types_1.isIdentifier)(prop.key) && !(0, types_1.isStringLiteral)(prop.key)) {
|
||
return;
|
||
}
|
||
if (!(0, types_1.isIdentifier)(prop.value)) {
|
||
return;
|
||
}
|
||
bindingComponents[prop.value.name] = {
|
||
tag: (0, types_1.isIdentifier)(prop.key) ? prop.key.name : prop.key.value,
|
||
type: 'unknown',
|
||
};
|
||
});
|
||
},
|
||
});
|
||
return bindingComponents;
|
||
}
|
||
/**
|
||
* vue component imports
|
||
* @param filename
|
||
* @param imports
|
||
* @param resolve
|
||
* @returns
|
||
*/
|
||
async function parseVueComponentImports(importer, imports, resolve) {
|
||
const vueComponentImports = [];
|
||
for (let i = 0; i < imports.length; i++) {
|
||
const { source } = imports[i];
|
||
if ((0, utils_2.parseVueRequest)(source.value).query.vue) {
|
||
continue;
|
||
}
|
||
const resolveId = await resolve(source.value, importer);
|
||
if (!resolveId) {
|
||
continue;
|
||
}
|
||
const { filename } = (0, utils_2.parseVueRequest)(resolveId.id);
|
||
if (constants_1.EXTNAME_VUE_RE.test(filename)) {
|
||
source.value = resolveId.id;
|
||
vueComponentImports.push(imports[i]);
|
||
}
|
||
}
|
||
return vueComponentImports;
|
||
}
|
||
/**
|
||
* static import => dynamic import
|
||
* @param code
|
||
* @param imports
|
||
* @param dynamicImport
|
||
* @returns
|
||
*/
|
||
async function transformDynamicImports(code, imports, { id, sourceMap, dynamicImport, }) {
|
||
if (!imports.length) {
|
||
return {
|
||
code,
|
||
map: null,
|
||
};
|
||
}
|
||
const s = new magic_string_1.default(code);
|
||
for (let i = 0; i < imports.length; i++) {
|
||
const { start, end, specifiers: [specifier], source, } = imports[i];
|
||
s.overwrite(start, end, dynamicImport(specifier.local.name, source.value) + ';');
|
||
}
|
||
return {
|
||
code: s.toString(),
|
||
map: null,
|
||
};
|
||
}
|
||
exports.transformDynamicImports = transformDynamicImports;
|