UNPKG

@template-tools/template-sync

Version:
307 lines (260 loc) 7.9 kB
import recast from "recast"; import parser from "recast/parsers/babel.js"; import transform from "@babel/core/lib/transform.js"; import { StringContentEntry } from "content-entry"; import { Merger } from "../merger.mjs"; export class Rollup extends Merger { static get pattern() { return "**/rollup.config.*js"; } static get options() { return { ...super.options, messagePrefix: "chore(rollup): ", optionalDevDependencies: [ /\@rollup\/plugin\-.*/, /rollup\-plugin\-.*/, /babel-preset.*/, "builtin-modules", /^postcss-import$/ ] }; } static async usedDevDependencies(into, entry) { const content = await entry.string; const ast = recast.parse(content, { parser: { parse: source => transform.transform(source, { code: false, ast: true, sourceMap: false }).ast } }); for (const decl of ast.program.body) { if (decl.type === "ImportDeclaration") { into.add(decl.source.value); } } return into; } static async *commits( context, destinationEntry, sourceEntry, options = this.options ) { const name = destinationEntry.name; const templateContent = await sourceEntry.string; if (await destinationEntry.isEmpty) { yield { message: `${options.messagePrefix}add missing ${destinationEntry.name} from template`, entries: [ new StringContentEntry( destinationEntry.name, undefined, context.expand(templateContent, options.expand) ) ] }; return; } const original = await destinationEntry.string; let messages = []; const templateAST = recast.parse(templateContent, parser); const ast = recast.parse(original, parser); removeUseStrict(ast); const exp = exportDefaultDeclaration(ast); const templateExp = exportDefaultDeclaration(templateAST); if (exp?.properties !== undefined && templateExp !== undefined) { let output, dest; const banner = removePropertiesKey(exp.properties, "banner"); for (const p of exp.properties) { switch (p.key.name) { case "targets": dest = p.value.elements[0].properties[0]; //.find(x => x.name === 'dest'); const op = findProperty(templateExp.properties, "output"); if (op !== undefined) { p.key.name = "output"; p.value = op.value; output = p; } break; case "entry": const ip = findProperty(templateExp.properties, "input"); if (ip !== undefined) { p.key.name = "input"; p.value = ip.value; } } } if (findProperty(exp.properties, "input") === undefined) { exp.properties.push(findProperty(templateExp.properties, "input")); } const originalOutput = findProperty(exp.properties, "output"); const templateOutput = findProperty(templateExp.properties, "output"); if (originalOutput === undefined) { exp.properties.push(templateOutput); } else { mergeKeys( templateOutput, originalOutput, [ "format", "file", "dir", "name", "globals", "paths", "banner", "footer", "intro", "outro", "sourcemap", "sourcemapFile", "interop", "extend", "exports", "amd", "indent", "strict", "freeze", "legacy", "namespaceToStringTag" ], messages, options ); if (output !== undefined) { if (banner !== undefined) { output.value.properties.push(banner); } if (dest !== undefined) { const file = findProperty(output.value.properties, "file"); if (file !== undefined) { file.value = dest.value; } } } removePropertiesKey(exp.properties, "format"); removePropertiesKey(exp.properties, "sourceMap"); removePropertiesKey(exp.properties, "dest"); } } const originalImports = importDeclarationsByLocalName(ast); const templateImports = importDeclarationsByLocalName(templateAST); const addedImports = []; templateImports.forEach((value, key) => { if (originalImports.get(key) === undefined) { ast.program.body = [value, ...ast.program.body]; addedImports.push(key); } }); if (addedImports.length > 0) { messages.push(`${options.messagePrefix}import ${addedImports.join(",")}`); } const addedPlugins = []; const originalPlugins = pluginsFromExpression(exp); const templatePlugins = pluginsFromExpression(templateExp); templatePlugins.forEach(templatePlugin => { if ( templatePlugin && templatePlugin.callee !== undefined && originalPlugins.find( op => op?.callee?.name === templatePlugin.callee.name ) === undefined ) { originalPlugins.push(templatePlugin); addedPlugins.push(templatePlugin.callee.name); } }); if (addedPlugins.length > 0) { messages.push(`${options.messagePrefix}add ${addedPlugins.join(",")}`); } const merged = recast.print(ast).code; if (original !== merged) { yield { entries: [new StringContentEntry(name, undefined, merged)], message: messages.join("\n") }; } } } function removeUseStrict(ast) { for (const i in ast.program.body) { const decl = ast.program.body[i]; if ( decl.type === "ExpressionStatement" && decl.expression.type === "Literal" && decl.expression.value === "use strict" ) { ast.program.body.splice(i, 1); return; } } } function importDeclarationsByLocalName(ast) { const declarations = new Map(); for (const decl of ast.program.body) { if (decl.type === "ImportDeclaration") { for (const spec of decl.specifiers) { declarations.set(spec.local.name, decl); } } } return declarations; } function exportDefaultDeclaration(ast) { for (const decl of ast.program.body) { if (decl.type === "ExportDefaultDeclaration") { return decl.declaration; } } return undefined; } function pluginsFromExpression(exp) { const plugins = exp?.properties?.find(p => p?.key.name === "plugins"); if (plugins !== undefined) { return plugins.value.elements; } return []; } function findProperty(properties, name) { if (properties !== undefined) { const prop = properties.find(prop => prop?.key.name === name); return prop; } return undefined; } function removePropertiesKey(properties, name) { if (properties === undefined) { return undefined; } const toBeRemoved = properties.findIndex(x => x && x.key.name === name); if (toBeRemoved >= 0) { const slot = properties[toBeRemoved]; properties.splice(toBeRemoved, 1); return slot; } return undefined; } function mergeKeys(source, dest, knownKeys, messages, options) { const mergedKeys = []; if (source !== undefined) { knownKeys.forEach(key => { const destProp = findProperty(dest.value.properties, key); const sourceProp = findProperty(source.value.properties, key); if (sourceProp !== undefined && destProp === undefined) { mergedKeys.push(key); dest.value.properties.push(sourceProp); } }); } if (mergedKeys.length > 0) { messages.push( `${options.messagePrefix}add to output ${mergedKeys.join(",")}` ); } return mergedKeys; }