UNPKG

fuse-box

Version:

Fuse-Box a bundler that does it right

156 lines (125 loc) 4.17 kB
import { ASTTraverse } from "./../ASTTraverse"; import { PrettyError } from "./../PrettyError"; import { File } from "../core/File"; import * as acorn from "acorn"; import { AutoImport } from "./plugins/AutoImport"; import { OwnVariable } from "./plugins/OwnVariable"; import { OwnBundle } from "./plugins/OwnBundle"; import { ImportDeclaration } from "./plugins/ImportDeclaration"; import { DynamicImportStatement } from "./plugins/DynamicImportStatement"; require("acorn-es7")(acorn); require("acorn-jsx/inject")(acorn); export interface TraversalPlugin { onNode(file: File, node: any, parent: any): void onEnd(file: File): void } const plugins: TraversalPlugin[] = [AutoImport, OwnVariable, OwnBundle, ImportDeclaration, DynamicImportStatement]; export function acornParse(contents, options?: any) { return acorn.parse(contents, { ...options || {}, ...{ sourceType: "module", tolerant: true, ecmaVersion: 8, plugins: { es7: true, jsx: true }, jsx: { allowNamespacedObjects: true }, }, }); } /** * Makes static analysis on the code * Gets require statements (es5 and es6) * * Adds additional injections (if needed) * * @export * @class FileAST */ export class FileAnalysis { public ast: any; private wasAnalysed = false; private skipAnalysis = false; public bannedImports = {}; public nativeImports = {}; public fuseBoxMainFile; public requiresRegeneration = false; public fuseBoxVariable = "FuseBox"; public dependencies: string[] = []; constructor(public file: File) { } public astIsLoaded(): boolean { return this.ast !== undefined; } /** * Loads an AST * * @param {*} ast * * @memberOf FileAnalysis */ public loadAst(ast: any) { this.ast = ast; } public skip() { this.skipAnalysis = true; } /** * * * @private * * @memberOf FileAST */ public parseUsingAcorn(options?: any) { try { this.ast = acornParse(this.file.contents, options); } catch (err) { return PrettyError.errorWithContents(err, this.file); } } public handleAliasReplacement(requireStatement: string): string { if (!this.file.context.experimentalAliasEnabled) { return requireStatement; } // enable aliases only for the current project // if (this.file.collection.name !== this.file.context.defaultPackageName) { // return requireStatement; // } const aliasCollection = this.file.context.aliasCollection; aliasCollection.forEach(props => { if (props.expr.test(requireStatement)) { requireStatement = requireStatement.replace(props.expr, `${props.replacement}$2`); // only if we need it this.requiresRegeneration = true; } }); return requireStatement; } public addDependency(name: string) { this.dependencies.push(name); } public resetDependencies() { this.dependencies = []; } public nodeIsString(node) { return node.type === "Literal" || node.type === "StringLiteral"; } public analyze(traversalOptions?: { plugins: TraversalPlugin[] }) { // We don't want to make analysis 2 times if (this.wasAnalysed || this.skipAnalysis) { return; } let traversalPlugins = plugins if (traversalOptions && Array.isArray(traversalOptions.plugins)) { traversalPlugins = plugins.concat(traversalOptions.plugins) } ASTTraverse.traverse(this.ast, { pre: (node, parent, prop, idx) => traversalPlugins.forEach(plugin => plugin.onNode(this.file, node, parent)), }); traversalPlugins.forEach(plugin => plugin.onEnd(this.file)); this.wasAnalysed = true; // regenerate content if (this.requiresRegeneration) { this.file.contents = this.file.context.generateCode(this.ast); } } }