Skip to content

Commit b7ec34d

Browse files
Add tests for external rollup option support (#90)
* Upgrade to latest acorn * All tests passing
1 parent dccd350 commit b7ec34d

File tree

15 files changed

+269
-130
lines changed

15 files changed

+269
-130
lines changed

package-lock.json

Lines changed: 148 additions & 80 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"acorn": "6.0.2",
3131
"acorn-dynamic-import": "4.0.0",
3232
"acorn-walk": "6.1.0",
33-
"google-closure-compiler": "20180910.1.0",
33+
"google-closure-compiler": "20181008.0.0",
3434
"magic-string": "0.25.1",
3535
"temp-write": "3.4.0"
3636
},

src/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import compiler from './compiler';
2929
import options from './options';
3030
import { preCompilation, createTransforms, deriveFromInputSource } from './transforms';
3131
import { Transform } from './types';
32-
import { logSource } from './debug';
3332

3433
const readFile = promisify(fs.readFile);
3534

@@ -48,7 +47,6 @@ const renderChunk = async (
4847
outputOptions: OutputOptions,
4948
): Promise<{ code: string; map: RawSourceMap } | void> => {
5049
const code = await preCompilation(sourceCode, outputOptions, transforms);
51-
logSource('transform', sourceCode, code);
5250
const [compileOptions, mapFile] = options(
5351
requestedCompileOptions,
5452
outputOptions,

src/transformers/exports.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ import {
3232
ExportClosureMapping,
3333
} from '../types';
3434
import MagicString from 'magic-string';
35-
const acornWalk = require('acorn-walk');
36-
const inject = require('acorn-dynamic-import/lib/walk').default;
37-
const walk = inject(acornWalk);
35+
const walk = require('acorn-dynamic-import/lib/walk').default(require('acorn-walk'));
3836

3937
/**
4038
* This Transform will apply only if the Rollup configuration is for 'esm' output.
@@ -143,6 +141,7 @@ export default class ExportTransform extends Transform implements TransformInter
143141
const originalExportIdentifiers = Object.keys(originalExports);
144142

145143
source.trimEnd();
144+
146145
walk.ancestor(program, {
147146
// We inserted window scoped assignments for all the export statements during `preCompilation`
148147
// window['exportName'] = exportName;

src/transformers/imports.ts

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,47 +14,60 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { Transform, ALL_IMPORT_DECLARATIONS, IMPORT_DECLARATION } from '../types';
18-
import { literalName } from './parsing-utilities';
17+
import { Transform } from '../types';
18+
import { literalName, importLocalNames } from './parsing-utilities';
1919
import { TransformSourceDescription } from 'rollup';
2020
import MagicString from 'magic-string';
21+
import { ImportDeclaration } from 'estree';
22+
const walk = require('acorn-dynamic-import/lib/walk').default(require('acorn-walk'));
2123

22-
// TODO(KB): Need to generate externs for external members if we do not have an extern passed in.
23-
// Otherwise, advanced mode compilation will fail.
24-
// Likely this means moving scanning for external imports from `preCompilation` to the `derive` phase.
25-
// const HEADER = `/**
26-
// * @fileoverview Externs built via derived configuration from Rollup or input code.
27-
// * This extern contains the external import names, to prevent compilation failures.
28-
// * @externs
29-
// */
30-
// `;
24+
const HEADER = `/**
25+
* @fileoverview Externs built via derived configuration from Rollup or input code.
26+
* This extern contains the external import names, to prevent compilation failures.
27+
* @externs
28+
*/
29+
`;
3130

3231
export default class ImportTransform extends Transform {
3332
private importedExternalsSyntax: { [key: string]: string } = {};
33+
private importedExternalsLocalNames: Array<string> = [];
3434

3535
/**
3636
* Rollup allows configuration for 'external' imports.
3737
* These are items that will not be bundled with the resulting code, but instead are expected
3838
* to already be available via `import` or other means.
3939
* @param source parsed from import statements, this is the source of a particular import statement.
40-
* @return Promise<boolean> if the import is listed in the external list.
40+
* @return boolean if the import is listed in the external list.
4141
*/
42-
private async isExternalImport(source: string): Promise<boolean> {
42+
private isExternalImport(source: string): boolean {
4343
if (this.inputOptions.external === undefined) {
4444
return false;
4545
}
4646
if (Array.isArray(this.inputOptions.external)) {
4747
return this.inputOptions.external.includes(source);
4848
}
49-
// TODO(KB): Restore if needed.
50-
// if (typeof this.inputOptions.external === 'function') {
51-
// const configDrivenExternal = await this.inputOptions.external(source, parent, true);
52-
// return configDrivenExternal !== undefined && configDrivenExternal === true;
53-
// }
5449

5550
return false;
5651
}
5752

53+
/**
54+
* Generate externs for local names of external imports.
55+
* Otherwise, advanced mode compilation will fail since the reference is unknown.
56+
* @return string representing content of generated extern.
57+
*/
58+
public extern(): string {
59+
if (this.importedExternalsLocalNames.length > 0) {
60+
let extern = HEADER;
61+
this.importedExternalsLocalNames.forEach(name => {
62+
extern += `function ${name}(){};\n`;
63+
});
64+
65+
return extern;
66+
}
67+
68+
return '';
69+
}
70+
5871
/**
5972
* Before Closure Compiler modifies the source, we need to ensure external imports have been removed
6073
* since Closure will error out when it encounters them.
@@ -64,24 +77,25 @@ export default class ImportTransform extends Transform {
6477
* @return modified input source with external imports removed.
6578
*/
6679
public async preCompilation(code: string): Promise<TransformSourceDescription> {
80+
const self = this;
6781
const source = new MagicString(code);
68-
const program = this.context.parse(code, { ranges: true });
69-
const importNodes = program.body.filter(node => ALL_IMPORT_DECLARATIONS.includes(node.type));
82+
const program = self.context.parse(code, { ranges: true });
7083

71-
for (const node of importNodes) {
72-
switch (node.type) {
73-
case IMPORT_DECLARATION:
74-
const name = literalName(this.context, node.source);
75-
if (await this.isExternalImport(name)) {
76-
const range: [number, number] = node.range ? [node.range[0], node.range[1]] : [0, 0];
77-
this.importedExternalsSyntax[name] = code.slice(range[0], range[1]);
78-
source.remove(range[0], range[1]);
79-
}
80-
break;
81-
default:
82-
break;
83-
}
84-
}
84+
walk.simple(program, {
85+
async ImportDeclaration(node: ImportDeclaration) {
86+
const name = literalName(self.context, node.source);
87+
88+
if (self.isExternalImport(name)) {
89+
const range: [number, number] = node.range ? [node.range[0], node.range[1]] : [0, 0];
90+
self.importedExternalsSyntax[name] = code.slice(range[0], range[1]);
91+
source.remove(range[0], range[1]);
92+
93+
self.importedExternalsLocalNames = self.importedExternalsLocalNames.concat(
94+
importLocalNames(self.context, node),
95+
);
96+
}
97+
},
98+
});
8599

86100
return {
87101
code: source.toString(),

src/transformers/literal-computed-keys.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ import { Transform } from '../types';
1818
import { TransformSourceDescription } from 'rollup';
1919
import MagicString from 'magic-string';
2020
import { ObjectExpression } from 'estree';
21-
const acornWalk = require('acorn-walk');
22-
const inject = require('acorn-dynamic-import/lib/walk').default;
23-
const walk = inject(acornWalk);
21+
const walk = require('acorn-dynamic-import/lib/walk').default(require('acorn-walk'));
2422

2523
/**
2624
* Closure Compiler will not transform computed keys with literal values back to the literal value.

src/transformers/parsing-utilities.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,21 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { ExportNamedDeclaration, ExportDefaultDeclaration, Literal, SimpleLiteral } from 'estree';
17+
import {
18+
ExportNamedDeclaration,
19+
ExportDefaultDeclaration,
20+
Literal,
21+
SimpleLiteral,
22+
ImportDeclaration,
23+
} from 'estree';
1824
import { PluginContext } from 'rollup';
19-
import { ExportNameToClosureMapping, ExportClosureMapping } from '../types';
25+
import {
26+
ExportNameToClosureMapping,
27+
ExportClosureMapping,
28+
IMPORT_SPECIFIER,
29+
IMPORT_NAMESPACE_SPECIFIER,
30+
IMPORT_DEFAULT_SPECIFIER,
31+
} from '../types';
2032

2133
type ExportDeclarationsWithFunctions = ExportNamedDeclaration | ExportDefaultDeclaration;
2234

@@ -227,3 +239,26 @@ export function literalName(context: PluginContext, literal: Literal): string {
227239
const literalValue = (literal as SimpleLiteral).value;
228240
return typeof literalValue === 'string' ? literalValue : '';
229241
}
242+
243+
export function importLocalNames(
244+
context: PluginContext,
245+
declaration: ImportDeclaration,
246+
): Array<string> {
247+
const returnableSpecifiers: Array<string> = [];
248+
249+
if (declaration.specifiers) {
250+
declaration.specifiers.forEach(specifier => {
251+
switch (specifier.type) {
252+
case IMPORT_SPECIFIER:
253+
case IMPORT_NAMESPACE_SPECIFIER:
254+
case IMPORT_DEFAULT_SPECIFIER:
255+
returnableSpecifiers.push(specifier.local.name);
256+
break;
257+
default:
258+
break;
259+
}
260+
});
261+
}
262+
263+
return returnableSpecifiers;
264+
}

src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ import {
2626

2727
// @see https://github.com/estree/estree/blob/master/es2015.md#imports
2828
export const IMPORT_DECLARATION = 'ImportDeclaration';
29+
export const DYNAMIC_IMPORT_DECLARATION = 'Import';
2930
export const IMPORT_SPECIFIER = 'ImportSpecifier';
3031
export const IMPORT_DEFAULT_SPECIFIER = 'ImportDefaultSpecifier';
3132
export const IMPORT_NAMESPACE_SPECIFIER = 'ImportNamespaceSpecifier';
32-
export const ALL_IMPORT_DECLARATIONS = [IMPORT_DECLARATION];
33+
export const ALL_IMPORT_DECLARATIONS = [IMPORT_DECLARATION, DYNAMIC_IMPORT_DECLARATION];
3334

3435
// @see https://github.com/estree/estree/blob/master/es2015.md#exports
3536
export const EXPORT_NAMED_DECLARATION = 'ExportNamedDeclaration';

test/export-transpilation/fixtures/named-constant.esm.es5.js

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/generator.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ function generate(
6464
const bundle = await rollup.rollup({
6565
input: fixtureLocation(category, name, format, optionKey, false),
6666
plugins: [compiler(closureFlags[optionKey])],
67+
external: ['lodash'],
6768
});
6869

6970
return {

0 commit comments

Comments
 (0)