Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul export support to allow for manual mangling of reserved words #105

Closed
wants to merge 33 commits into from
Closed
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
88c23af
Linting
kristoferbaxter Nov 7, 2018
cd49246
Fix export default of imported member
kristoferbaxter Nov 7, 2018
4f35128
Fixed reserved-words tests
kristoferbaxter Nov 7, 2018
0fb6b0d
Only one remaining unhandled exception
kristoferbaxter Nov 7, 2018
ab3b21f
Remove extra commented out methods
kristoferbaxter Nov 7, 2018
bb3518c
Re-enabled and fixed results for es5 and advanced tests
kristoferbaxter Nov 7, 2018
f66380d
Fix final broken test
kristoferbaxter Nov 7, 2018
3b189cb
Remove blocks of text
kristoferbaxter Nov 7, 2018
88f07eb
Remove console from reserved-words
kristoferbaxter Nov 7, 2018
47603db
Prettier .js
kristoferbaxter Nov 7, 2018
a4b0f29
Fix reserved-word example spacing
kristoferbaxter Nov 7, 2018
8aa304e
Make mangle and remedy have the same api
kristoferbaxter Nov 7, 2018
cb93077
One less MagicString import
kristoferbaxter Nov 7, 2018
1ea5651
Move remaining code over to collection of changes, then application i…
kristoferbaxter Nov 7, 2018
4812bf8
Merge branch 'master' of github.com:ampproject/rollup-plugin-closure-…
kristoferbaxter Nov 7, 2018
91e7ece
Resolve new type errors
kristoferbaxter Nov 7, 2018
c4b5f29
Remove race condition trigger
kristoferbaxter Nov 7, 2018
382ef7e
Updated README with instructions on using mangleReservedWords
kristoferbaxter Nov 7, 2018
360012f
Extract logic for renewing mangled exports
kristoferbaxter Nov 7, 2018
895f035
Fix declaration generation
kristoferbaxter Nov 7, 2018
a5736c7
Merge branch 'master' of github.com:ampproject/rollup-plugin-closure-…
kristoferbaxter Nov 9, 2018
b1eef03
Mangle const to let
kristoferbaxter Nov 9, 2018
53d1a5a
for of versus forEach for early return
kristoferbaxter Nov 9, 2018
ff0b98c
Fix for multiple variable exports shared on a single variable declara…
kristoferbaxter Nov 10, 2018
522396d
collapse-declarations optimization
kristoferbaxter Nov 11, 2018
f8b1f1d
Reordering doesnt hoise defintions from within other contexts
kristoferbaxter Nov 12, 2018
ce9e2c2
beta 4
kristoferbaxter Nov 12, 2018
4a56d8f
Inverted condition
kristoferbaxter Nov 12, 2018
d8d56e1
beta 5
kristoferbaxter Nov 12, 2018
f9fb2a4
Disable experimental collapse declarations
kristoferbaxter Nov 12, 2018
c13135e
beta 6
kristoferbaxter Nov 12, 2018
4702a43
beta 7
kristoferbaxter Nov 12, 2018
013827d
Only renew if the export wasnt already discovered
kristoferbaxter Nov 15, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,28 @@ export default {
}
```

### Additional Mangle Protection

If your source uses reserved words for identifiers or declarations, you can supply the list of words you would like to ensure are mangled before compilation, and renewed if they are used in an import or export statement.

```js
// rollup.config.js
import compiler from '@ampproject/rollup-plugin-closure-compiler';

export default {
input: 'main.js',
output: {
file: 'bundle.js',
format: 'es',
},
plugins: [
compiler({}, {
mangleReservedWords: ['Plugin', 'Storage'],
}),
],
}
```

## Security disclosures

The AMP Project accepts responsible security disclosures through the [Google Application Security program](https://www.google.com/about/appsecurity/).
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ampproject/rollup-plugin-closure-compiler",
"version": "0.8.3",
"version": "0.9.0-beta.5",
"description": "Rollup + Google Closure Compiler",
"main": "dist/index.js",
"jsnext:main": "dist/index.mjs",
Expand Down Expand Up @@ -52,7 +52,8 @@
"files": [
"dist/index.d.ts",
"dist/index.js",
"dist/index.mjs"
"dist/index.mjs",
"dist/types.ts"
],
"lint-staged": {
"*.ts": [
Expand Down
44 changes: 26 additions & 18 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,35 @@ import pkg from './package.json';
import builtins from 'builtins';
import copy from 'rollup-plugin-copy';

export default {
input: './transpile/index.js',
external: [
...builtins(),
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
],
plugins: [
copy({
'./transpile/index.d.ts': './dist/index.d.ts',
verbose: true,
}),
],
output: [
{
const input = './transpile/index.js';
const external = [
...builtins(),
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
];

export default [
{
input,
external,
plugins: [
copy({
'./transpile/index.d.ts': './dist/index.d.ts',
'./src/types.ts': './dist/types.ts',
verbose: true,
}),
],
output: {
file: './dist/index.mjs',
format: 'es',
},
{
},
{
input,
external,
output: {
file: './dist/index.js',
format: 'cjs',
},
],
};
}
]
7 changes: 6 additions & 1 deletion src/acorn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

import { Program } from 'estree';
import { DYNAMIC_IMPORT_DECLARATION } from './types';
import { DYNAMIC_IMPORT_DECLARATION, SourceRange } from './types';
const acorn = require('acorn');
const acornWalk = require('acorn-walk');
const dynamicImport = require('acorn-dynamic-import');
Expand All @@ -38,8 +38,13 @@ const DEFAULT_ACORN_OPTIONS = {
sourceType: 'module',
preserveParens: false,
ranges: true,
locations: true,
};

export function parse(source: string): Program {
return acorn.Parser.extend(dynamicImport.default).parse(source, DEFAULT_ACORN_OPTIONS);
}

export function range(node: any): SourceRange {
return node.range ? [node.range[0], node.range[1]] : [0, 0];
}
8 changes: 6 additions & 2 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ const {
getNativeImagePath,
getFirstSupportedPlatform,
} = require('google-closure-compiler/lib/utils.js');
import { Transform } from './types';
import { postCompilation } from './transforms';
import { Transform } from './transformers/transform';
import { RenderedChunk } from 'rollup';
import { MangledWords } from './types';

enum Platform {
NATIVE = 'native',
Expand All @@ -38,7 +40,9 @@ const PLATFORM_PRECEDENCE = [Platform.NATIVE, Platform.JAVA, Platform.JAVASCRIPT
*/
export default function(
compileOptions: CompileOptions,
chunk: RenderedChunk,
transforms: Array<Transform>,
moduleMangledWords: MangledWords,
): Promise<string> {
return new Promise((resolve: (stdOut: string) => void, reject: (error: any) => void) => {
const instance = new compiler(compileOptions);
Expand All @@ -62,7 +66,7 @@ export default function(
} else if (exitCode !== 0) {
reject(new Error(`Google Closure Compiler exit ${exitCode}: ${stdErr}`));
} else {
resolve(await postCompilation(code, transforms));
resolve(await postCompilation(code, chunk, transforms, moduleMangledWords));
}
});
});
Expand Down
6 changes: 3 additions & 3 deletions src/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

import { sync } from 'temp-write';

const DEBUG_ENABLED = false;
const DEBUG_ENABLED: boolean = false;

export const logSource = (preamble: string, source: string, code?: string) => {
export const logSource = (preamble: string, source: string, code?: string): void => {
if (DEBUG_ENABLED) {
console.log(preamble);
console.log(sync(source));
Expand All @@ -28,7 +28,7 @@ export const logSource = (preamble: string, source: string, code?: string) => {
}
};

export const log = (preamble: string, message: string): void | null => {
export const log = (preamble: string, message: string): void => {
if (DEBUG_ENABLED) {
console.log(preamble);
console.log(message);
Expand Down
34 changes: 24 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import {
import compiler from './compiler';
import options from './options';
import { preCompilation, createTransforms } from './transforms';
import { Transform } from './types';
import { Transform } from './transformers/transform';
import { TransformOptions, MangledWords } from './types';

const readFile = promisify(fs.readFile);

Expand All @@ -40,33 +41,39 @@ const readFile = promisify(fs.readFile);
* @param outputOptions Rollup Output Options.
* @return Closure Compiled form of the Rollup Chunk
*/
const renderChunk = async (
async function renderChunk(
transforms: Array<Transform>,
requestedCompileOptions: CompileOptions = {},
sourceCode: string,
chunk: RenderedChunk,
outputOptions: OutputOptions,
): Promise<{ code: string; map: RawSourceMap } | void> => {
const code = await preCompilation(sourceCode, outputOptions, transforms);
globallyMangledWords: MangledWords,
): Promise<{ code: string; map: RawSourceMap } | void> {
const preCompiled = await preCompilation(sourceCode, chunk, transforms, globallyMangledWords);
const [compileOptions, mapFile] = options(
requestedCompileOptions,
outputOptions,
code,
preCompiled.code,
transforms,
);

return compiler(compileOptions, transforms).then(
return compiler(compileOptions, chunk, transforms, preCompiled.moduleMangledWords).then(
async code => {
return { code, map: JSON.parse(await readFile(mapFile, 'utf8')) };
},
(error: Error) => {
throw error;
},
);
};
}

export default function closureCompiler(requestedCompileOptions: CompileOptions = {}): Plugin {
export default function closureCompiler(
requestedCompileOptions: CompileOptions = {},
transformOptions: TransformOptions = {},
): Plugin {
let inputOptions: InputOptions;
let context: PluginContext;
const globallyMangledWords: MangledWords = new MangledWords(transformOptions.mangleReservedWords);

return {
name: 'closure-compiler',
Expand All @@ -84,8 +91,15 @@ export default function closureCompiler(requestedCompileOptions: CompileOptions
}
},
renderChunk: async (code: string, chunk: RenderedChunk, outputOptions: OutputOptions) => {
const transforms = createTransforms(context, inputOptions);
return await renderChunk(transforms, requestedCompileOptions, code, outputOptions);
const transforms = createTransforms(context, inputOptions, outputOptions, transformOptions);
return await renderChunk(
transforms,
requestedCompileOptions,
code,
chunk,
outputOptions,
globallyMangledWords,
);
},
};
}
83 changes: 83 additions & 0 deletions src/mangle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* 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 { SourceRange, MangledWords, CodeTransform } from './types';
import { walk, range } from './acorn';
import {
Program,
ClassDeclaration,
Identifier,
VariableDeclaration,
FunctionDeclaration,
} from 'estree';

export function mangleWord(name: string, range: SourceRange, mangled: MangledWords): CodeTransform {
const mangleName = mangled.getFinal(name);
if (mangleName) {
mangled.store(name, mangleName);
return {
type: 'overwrite',
range,
content: mangleName,
};
}

return null;
}

function remedyWord(name: string, range: SourceRange, mangled: MangledWords): CodeTransform {
const remedyWord = mangled.getInitial(name);
if (remedyWord) {
return {
type: 'overwrite',
range,
content: remedyWord,
};
}

return null;
}
export async function remedy(
program: Program,
mangled: MangledWords,
): Promise<Array<CodeTransform>> {
const changes: Array<CodeTransform> = [];

walk.simple(program, {
ClassDeclaration(node: ClassDeclaration) {
if (node.id !== null) {
changes.push(remedyWord(node.id.name, range(node.id), mangled));
}
},
Identifier(node: Identifier) {
changes.push(remedyWord(node.name, range(node), mangled));
},
VariableDeclaration(node: VariableDeclaration) {
node.declarations.forEach(declarator => {
if (declarator.id.type === 'Identifier') {
changes.push(remedyWord(declarator.id.name, range(declarator.id), mangled));
}
});
},
FunctionDeclaration(node: FunctionDeclaration) {
if (node.id) {
changes.push(remedyWord(node.id.name, range(node.id), mangled));
}
},
});

return changes;
}
2 changes: 1 addition & 1 deletion src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
* limitations under the License.
*/

import { Transform } from './types';
import { ModuleFormat, OutputOptions } from 'rollup';
import { CompileOptions } from 'google-closure-compiler';
import { sync } from 'temp-write';
import { logSource } from './debug';
import { Transform } from './transformers/transform';

export const ERROR_WARNINGS_ENABLED_LANGUAGE_OUT_UNSPECIFIED =
'Providing the warning_level=VERBOSE compile option also requires a valid language_out compile option.';
Expand Down
40 changes: 40 additions & 0 deletions src/parsers/ExportDefaultDeclaration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* 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 { ExportDefaultDeclaration } from 'estree';
import { DiscoveredExport } from '../types';
import { range } from '../acorn';

/**
* Find information about the ExportDefaultDeclaration passed
* @param node ExportDefaultDeclaration
* @return Interesting information about the default export.
*/
export function exportDefaultParser(node: ExportDefaultDeclaration): DiscoveredExport | null {
const declaration = node.declaration;
if (declaration.type === 'Identifier') {
// This is a default export of a known identifier
// Rollup will call unnamed default exports 'main' or another safeword.
return {
local: declaration.name,
exported: declaration.name,
default: true,
range: range(node),
};
}

return null;
}
Loading