|
|
import deindent from '../../../utils/deindent';
|
|
|
import { CompileOptions, ModuleFormat, Node } from '../../../interfaces';
|
|
|
|
|
|
interface Dependency {
|
|
|
name: string;
|
|
|
statements: string[];
|
|
|
source: string;
|
|
|
}
|
|
|
|
|
|
export default function getModuleWrapper(
|
|
|
format: ModuleFormat,
|
|
|
name: string,
|
|
|
options: CompileOptions,
|
|
|
imports: Node[],
|
|
|
source: string
|
|
|
) {
|
|
|
if (format === 'es') return getEsWrapper(name, options, imports, source);
|
|
|
|
|
|
const dependencies = imports.map((declaration, i) => {
|
|
|
const defaultImport = declaration.specifiers.find(
|
|
|
(x: Node) =>
|
|
|
x.type === 'ImportDefaultSpecifier' ||
|
|
|
(x.type === 'ImportSpecifier' && x.imported.name === 'default')
|
|
|
);
|
|
|
|
|
|
const namespaceImport = declaration.specifiers.find(
|
|
|
(x: Node) => x.type === 'ImportNamespaceSpecifier'
|
|
|
);
|
|
|
|
|
|
const namedImports = declaration.specifiers.filter(
|
|
|
(x: Node) =>
|
|
|
x.type === 'ImportSpecifier' && x.imported.name !== 'default'
|
|
|
);
|
|
|
|
|
|
const name = defaultImport || namespaceImport
|
|
|
? (defaultImport || namespaceImport).local.name
|
|
|
: `__import${i}`;
|
|
|
|
|
|
const statements: string[] = [];
|
|
|
|
|
|
namedImports.forEach((specifier: Node) => {
|
|
|
statements.push(
|
|
|
`var ${specifier.local.name} = ${name}.${specifier.imported.name};`
|
|
|
);
|
|
|
});
|
|
|
|
|
|
if (defaultImport) {
|
|
|
statements.push(
|
|
|
`${name} = (${name} && ${name}.__esModule) ? ${name}["default"] : ${name};`
|
|
|
);
|
|
|
}
|
|
|
|
|
|
return { name, statements, source: declaration.source.value };
|
|
|
});
|
|
|
|
|
|
if (format === 'amd') return getAmdWrapper(name, options, dependencies);
|
|
|
if (format === 'cjs') return getCjsWrapper(name, options, dependencies);
|
|
|
if (format === 'iife') return getIifeWrapper(name, options, dependencies);
|
|
|
if (format === 'umd') return getUmdWrapper(name, options, dependencies);
|
|
|
if (format === 'eval') return getEvalWrapper(name, options, dependencies);
|
|
|
|
|
|
throw new Error(`Not implemented: ${format}`);
|
|
|
}
|
|
|
|
|
|
function getEsWrapper(name: string, options: CompileOptions, imports: Node[], source: string) {
|
|
|
const importBlock = imports
|
|
|
.map((declaration: Node) => source.slice(declaration.start, declaration.end))
|
|
|
.join('\n');
|
|
|
|
|
|
return {
|
|
|
intro: importBlock ? importBlock + '\n\n' : '',
|
|
|
outro: `export default ${name};`
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function getAmdWrapper(name: string, options: CompileOptions, dependencies: Dependency[]) {
|
|
|
const sourceString = dependencies.length
|
|
|
? `[${dependencies.map(d => `"${removeExtension(d.source)}"`).join(', ')}], `
|
|
|
: '';
|
|
|
|
|
|
const id = options.amd && options.amd.id;
|
|
|
|
|
|
return {
|
|
|
intro: deindent`
|
|
|
define(${id ? `"${id}", ` : ''}${sourceString}function(${paramString(dependencies)}) { "use strict";
|
|
|
|
|
|
${getCompatibilityStatements(dependencies)}
|
|
|
|
|
|
`,
|
|
|
outro: deindent`
|
|
|
return ${name};
|
|
|
});`
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function getCjsWrapper(name: string, options: CompileOptions, dependencies: Dependency[]) {
|
|
|
const requireBlock = dependencies
|
|
|
.map(d => `var ${d.name} = require("${d.source}");`)
|
|
|
.join('\n\n');
|
|
|
|
|
|
const intro = requireBlock ?
|
|
|
deindent`
|
|
|
"use strict";
|
|
|
|
|
|
${requireBlock}
|
|
|
${getCompatibilityStatements(dependencies)}
|
|
|
|
|
|
` :
|
|
|
deindent`
|
|
|
"use strict";
|
|
|
|
|
|
`;
|
|
|
|
|
|
const outro = `module.exports = ${name};`
|
|
|
|
|
|
return { intro, outro };
|
|
|
}
|
|
|
|
|
|
function getIifeWrapper(name: string, options: CompileOptions, dependencies: Dependency[]) {
|
|
|
if (!options.name) {
|
|
|
throw new Error(`Missing required 'name' option for IIFE export`);
|
|
|
}
|
|
|
|
|
|
const globals = getGlobals(dependencies, options);
|
|
|
|
|
|
return {
|
|
|
intro: deindent`
|
|
|
var ${options.name} = (function(${paramString(dependencies)}) { "use strict";
|
|
|
|
|
|
${getCompatibilityStatements(dependencies)}
|
|
|
|
|
|
`,
|
|
|
|
|
|
outro: `return ${name};\n\n}(${globals.join(', ')}));`
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function getUmdWrapper(name: string, options: CompileOptions, dependencies: Dependency[]) {
|
|
|
if (!options.name) {
|
|
|
throw new Error(`Missing required 'name' option for UMD export`);
|
|
|
}
|
|
|
|
|
|
const amdId = options.amd && options.amd.id ? `'${options.amd.id}', ` : '';
|
|
|
|
|
|
const amdDeps = dependencies.length
|
|
|
? `[${dependencies.map(d => `"${removeExtension(d.source)}"`).join(', ')}], `
|
|
|
: '';
|
|
|
|
|
|
const cjsDeps = dependencies
|
|
|
.map(d => `require("${d.source}")`)
|
|
|
.join(', ');
|
|
|
|
|
|
const globals = getGlobals(dependencies, options);
|
|
|
|
|
|
return {
|
|
|
intro: deindent`
|
|
|
(function(global, factory) {
|
|
|
typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory(${cjsDeps}) :
|
|
|
typeof define === "function" && define.amd ? define(${amdId}${amdDeps}factory) :
|
|
|
(global.${options.name} = factory(${globals}));
|
|
|
}(this, (function (${paramString(dependencies)}) { "use strict";
|
|
|
|
|
|
${getCompatibilityStatements(dependencies)}
|
|
|
|
|
|
`,
|
|
|
|
|
|
outro: deindent`
|
|
|
return ${name};
|
|
|
|
|
|
})));`
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function getEvalWrapper(name: string, options: CompileOptions, dependencies: Dependency[]) {
|
|
|
const globals = getGlobals(dependencies, options);
|
|
|
|
|
|
return {
|
|
|
intro: deindent`
|
|
|
(function (${paramString(dependencies)}) { "use strict";
|
|
|
|
|
|
${getCompatibilityStatements(dependencies)}
|
|
|
|
|
|
`,
|
|
|
|
|
|
outro: `return ${name};\n\n}(${globals.join(', ')}));`
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function paramString(dependencies: Dependency[]) {
|
|
|
return dependencies.map(dep => dep.name).join(', ');
|
|
|
}
|
|
|
|
|
|
function removeExtension(file: string) {
|
|
|
const index = file.lastIndexOf('.');
|
|
|
return ~index ? file.slice(0, index) : file;
|
|
|
}
|
|
|
|
|
|
function getCompatibilityStatements(dependencies: Dependency[]) {
|
|
|
if (!dependencies.length) return null;
|
|
|
|
|
|
const statements: string[] = [];
|
|
|
|
|
|
dependencies.forEach(dependency => {
|
|
|
statements.push(...dependency.statements);
|
|
|
});
|
|
|
|
|
|
return statements.join('\n');
|
|
|
}
|
|
|
|
|
|
function getGlobals(dependencies: Dependency[], options: CompileOptions) {
|
|
|
const { globals, onerror, onwarn } = options;
|
|
|
const globalFn = getGlobalFn(globals);
|
|
|
|
|
|
return dependencies.map(d => {
|
|
|
let name = globalFn(d.source);
|
|
|
|
|
|
if (!name) {
|
|
|
if (d.name.startsWith('__import')) {
|
|
|
const error = new Error(
|
|
|
`Could not determine name for imported module '${d.source}' – use options.globals`
|
|
|
);
|
|
|
if (onerror) {
|
|
|
onerror(error);
|
|
|
} else {
|
|
|
throw error;
|
|
|
}
|
|
|
} else {
|
|
|
const warning = {
|
|
|
message: `No name was supplied for imported module '${d.source}'. Guessing '${d.name}', but you should use options.globals`,
|
|
|
};
|
|
|
|
|
|
if (onwarn) {
|
|
|
onwarn(warning);
|
|
|
} else {
|
|
|
console.warn(warning); // eslint-disable-line no-console
|
|
|
}
|
|
|
}
|
|
|
|
|
|
name = d.name;
|
|
|
}
|
|
|
|
|
|
return name;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function getGlobalFn(globals: any): (id: string) => string {
|
|
|
if (typeof globals === 'function') return globals;
|
|
|
if (typeof globals === 'object') {
|
|
|
return id => globals[id];
|
|
|
}
|
|
|
|
|
|
return () => undefined;
|
|
|
} |