diff --git a/src/parsing/export-details.ts b/src/parsing/export-details.ts new file mode 100644 index 00000000..8680a82a --- /dev/null +++ b/src/parsing/export-details.ts @@ -0,0 +1,65 @@ +/** + * Copyright 2020 The AMP HTML Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ExportNamedDeclaration, ExportDefaultDeclaration } from 'estree'; +import { ExportDetails, Range, ExportClosureMapping } from '../types'; + +export function NamedDeclaration(declaration: ExportNamedDeclaration): Array { + const range: Range = declaration.range as Range; + const source: string | null = + typeof declaration?.source?.value === 'string' ? declaration.source.value : null; + + if (declaration.specifiers) { + const exportDetails: Array = []; + + for (const specifier of declaration.specifiers) { + exportDetails.push({ + local: specifier.local.name, + exported: specifier.exported.name, + closureName: specifier.exported.name, + type: ExportClosureMapping.NAMED_CONSTANT, + range, + source, + }); + } + + return exportDetails; + } + + return []; +} + +export function DefaultDeclaration( + defaultDeclaration: ExportDefaultDeclaration, +): Array { + const range: Range = defaultDeclaration.range as Range; + const { declaration } = defaultDeclaration; + + if (declaration.type === 'Identifier' && declaration.name) { + return [ + { + local: declaration.name, + exported: declaration.name, + closureName: declaration.name, + type: ExportClosureMapping.NAMED_DEFAULT_FUNCTION, + range, + source: null, + }, + ]; + } + + return []; +} diff --git a/src/parsing/literal-name.ts b/src/parsing/literal-name.ts new file mode 100644 index 00000000..7e3333ea --- /dev/null +++ b/src/parsing/literal-name.ts @@ -0,0 +1,32 @@ +/** + * Copyright 2020 The AMP HTML Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { PluginContext } from 'rollup'; +import { Literal, SimpleLiteral } from 'estree'; + +export function literalName(context: PluginContext, literal: Literal): string { + // Literal can either be a SimpleLiteral, or RegExpLiteral + if ('regex' in literal) { + // This is a RegExpLiteral + context.warn( + 'Rollup Plugin Closure Compiler found a Regex Literal Named Import. `import foo from "*/.hbs"`', + ); + return ''; + } + + const literalValue = (literal as SimpleLiteral).value; + return typeof literalValue === 'string' ? literalValue : ''; +} diff --git a/src/transformers/exports.ts b/src/transformers/exports.ts index 4e9d3dbc..76e3a595 100644 --- a/src/transformers/exports.ts +++ b/src/transformers/exports.ts @@ -22,7 +22,7 @@ import { Node, } from 'estree'; import { TransformSourceDescription } from 'rollup'; -import { NamedDeclaration, DefaultDeclaration } from './parsing-utilities'; +import { NamedDeclaration, DefaultDeclaration } from '../parsing/export-details'; import { isESMFormat } from '../options'; import { Transform, @@ -86,10 +86,10 @@ export default class ExportTransform extends Transform implements TransformInter walk.simple(program, { ExportNamedDeclaration(node: ExportNamedDeclaration) { - storeExport(NamedDeclaration(context, node)); + storeExport(NamedDeclaration(node)); }, ExportDefaultDeclaration(node: ExportDefaultDeclaration) { - storeExport(DefaultDeclaration(context, node)); + storeExport(DefaultDeclaration(node)); }, ExportAllDeclaration(node: ExportAllDeclaration) { // TODO(KB): This case `export * from "./import"` is not currently supported. diff --git a/src/transformers/imports.ts b/src/transformers/imports.ts index 33d0edd1..4eb0daf5 100644 --- a/src/transformers/imports.ts +++ b/src/transformers/imports.ts @@ -21,7 +21,7 @@ import { IMPORT_NAMESPACE_SPECIFIER, IMPORT_DEFAULT_SPECIFIER, } from '../types'; -import { literalName, importLocalNames } from './parsing-utilities'; +import { literalName } from '../parsing/literal-name'; import { TransformSourceDescription } from 'rollup'; import MagicString from 'magic-string'; import { ImportDeclaration, Identifier, ImportSpecifier } from 'estree'; @@ -42,6 +42,19 @@ interface RangedImport { range: Range; } +const VALID_SPECIFIERS = [IMPORT_SPECIFIER, IMPORT_NAMESPACE_SPECIFIER, IMPORT_DEFAULT_SPECIFIER]; +function importLocalNames(declaration: ImportDeclaration): Array { + const returnableSpecifiers: Array = []; + + for (const specifier of declaration.specifiers || []) { + if (VALID_SPECIFIERS.includes(specifier.type)) { + returnableSpecifiers.push(specifier.local.name); + } + } + + return returnableSpecifiers; +} + export default class ImportTransform extends Transform { private importedExternalsSyntax: { [key: string]: string } = {}; private importedExternalsLocalNames: Array = []; @@ -117,7 +130,7 @@ window['${DYNAMIC_IMPORT_REPLACEMENT}'] = ${DYNAMIC_IMPORT_REPLACEMENT};`; source.remove(...range); self.importedExternalsLocalNames = self.importedExternalsLocalNames.concat( - importLocalNames(self.context, node), + importLocalNames(node), ); }, Import(node: RangedImport) { diff --git a/src/transformers/parsing-utilities.ts b/src/transformers/parsing-utilities.ts deleted file mode 100644 index c0517b61..00000000 --- a/src/transformers/parsing-utilities.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright 2018 The AMP HTML Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS-IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - ExportNamedDeclaration, - ExportDefaultDeclaration, - Literal, - SimpleLiteral, - ImportDeclaration, -} from 'estree'; -import { PluginContext } from 'rollup'; -import { - ExportClosureMapping, - IMPORT_SPECIFIER, - IMPORT_NAMESPACE_SPECIFIER, - IMPORT_DEFAULT_SPECIFIER, - ExportDetails, - Range, -} from '../types'; - -export function NamedDeclaration( - context: PluginContext, - declaration: ExportNamedDeclaration, -): Array { - const range: Range = declaration.range as Range; - const source: string | null = - declaration.source && declaration.source.value && typeof declaration.source.value === 'string' - ? declaration.source.value - : null; - - if (declaration.specifiers) { - const exportDetails: Array = []; - - for (const specifier of declaration.specifiers) { - exportDetails.push({ - local: specifier.local.name, - exported: specifier.exported.name, - closureName: specifier.exported.name, - type: ExportClosureMapping.NAMED_CONSTANT, - range, - source, - }); - } - return exportDetails; - } - - return []; -} - -export function DefaultDeclaration( - context: PluginContext, - declaration: ExportDefaultDeclaration, -): Array { - if (declaration.declaration) { - const range: Range = declaration.range as Range; - const source = null; - - if (declaration.declaration.type === 'Identifier' && declaration.declaration.name) { - return [ - { - local: declaration.declaration.name, - exported: declaration.declaration.name, - closureName: declaration.declaration.name, - type: ExportClosureMapping.NAMED_DEFAULT_FUNCTION, - range, - source, - }, - ]; - } - } - - return []; -} - -export function literalName(context: PluginContext, literal: Literal): string { - // Literal can either be a SimpleLiteral, or RegExpLiteral - if ('regex' in literal) { - // This is a RegExpLiteral - context.warn( - 'Rollup Plugin Closure Compiler found a Regex Literal Named Import. `import foo from "*/.hbs"`', - ); - return ''; - } - - const literalValue = (literal as SimpleLiteral).value; - return typeof literalValue === 'string' ? literalValue : ''; -} - -export function importLocalNames( - context: PluginContext, - declaration: ImportDeclaration, -): Array { - const VALID_SPECIFIERS = [IMPORT_SPECIFIER, IMPORT_NAMESPACE_SPECIFIER, IMPORT_DEFAULT_SPECIFIER]; - const returnableSpecifiers: Array = []; - - (declaration.specifiers || []).forEach(specifier => { - if (VALID_SPECIFIERS.includes(specifier.type)) { - returnableSpecifiers.push(specifier.local.name); - } - }); - - return returnableSpecifiers; -} diff --git a/src/transformers/replacement-utilities.ts b/src/transformers/replacement-utilities.ts deleted file mode 100644 index 866ea054..00000000 --- a/src/transformers/replacement-utilities.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright 2018 The AMP HTML Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS-IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import MagicString from 'magic-string'; - -export function replace(from: string, to: string, code: string, source: MagicString): void { - const start = code.indexOf(from); - if (start >= 0) { - // If the from value is not part of the source, there is no need to remove it. - source.overwrite(start, start + from.length, to); - } else { - console.log('cannot remove', from, start); - } - // Should there be a warning? -} diff --git a/src/transformers/strict.ts b/src/transformers/strict.ts index 01ceff7c..d0abb879 100644 --- a/src/transformers/strict.ts +++ b/src/transformers/strict.ts @@ -19,7 +19,7 @@ import { isESMFormat } from '../options'; import { TransformSourceDescription } from 'rollup'; import MagicString from 'magic-string'; import { walk, parse } from '../acorn'; -import { ExpressionStatement } from 'estree'; +import { ExpressionStatement, SimpleLiteral } from 'estree'; import { extname } from 'path'; export default class StrictTransform extends Transform { @@ -32,25 +32,17 @@ export default class StrictTransform extends Transform { * @return code after removing the strict mode declaration (when safe to do so) */ public async postCompilation(code: string): Promise { - if (this.outputOptions === null) { - this.context.warn( - 'Rollup Plugin Closure Compiler, OutputOptions not known before Closure Compiler invocation.', - ); - } else if ( - isESMFormat(this.outputOptions.format) || - (this.outputOptions.file && extname(this.outputOptions.file) === '.mjs') - ) { + const { format, file } = this.outputOptions; + + if (isESMFormat(format) || (file && extname(file) === '.mjs')) { const source = new MagicString(code); const program = parse(code); walk.simple(program, { ExpressionStatement(node: ExpressionStatement) { - if ( - node.expression.type === 'Literal' && - node.expression.value === 'use strict' && - node.range - ) { - source.remove(node.range[0], node.range[1]); + const { type, value } = node.expression as SimpleLiteral; + if (type === 'Literal' && value === 'use strict' && node.range) { + source.remove(...node.range); } }, }); diff --git a/src/types.ts b/src/types.ts index df222816..bea28268 100644 --- a/src/types.ts +++ b/src/types.ts @@ -69,7 +69,7 @@ export interface TransformInterface { export class Transform implements TransformInterface { protected context: PluginContext; protected inputOptions: InputOptions; - public outputOptions: OutputOptions | null; + public outputOptions: OutputOptions; public name: string = 'Transform'; constructor(context: PluginContext, inputOptions: InputOptions) { @@ -92,21 +92,4 @@ export class Transform implements TransformInterface { code, }; } - - // TODO (KB): Is this needed? - // protected isEntryPoint(id: string) { - // const inputs = (input: InputOption): Array => { - // if (typeof input === 'string') { - // return [input]; - // } else if (typeof input === 'object') { - // return Object.values(input); - // } else { - // return input; - // } - // }; - - // return inputs(this.inputOptions.input) - // .map(input => path.resolve(input)) - // .includes(id); - // } } diff --git a/tsconfig.json b/tsconfig.json index b4c80d23..dfbda946 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "outDir": "transpile", "sourceMap": true, "moduleResolution": "node", - "target": "esnext", + "target": "es2019", "baseUrl": ".", "allowJs": false, "noUnusedLocals": true,