Skip to content

Commit c443664

Browse files
Option to remove strict mode declarations (#319)
1 parent 215d530 commit c443664

File tree

10 files changed

+149
-27
lines changed

10 files changed

+149
-27
lines changed

src/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,13 @@ export default function closureCompiler(requestedCompileOptions: CompileOptions
4545
options: options => (inputOptions = options),
4646
buildStart() {
4747
context = this;
48-
sourceTransforms = createSourceTransforms(context, mangler, inputOptions, {});
48+
sourceTransforms = createSourceTransforms(
49+
context,
50+
requestedCompileOptions,
51+
mangler,
52+
inputOptions,
53+
{},
54+
);
4955
if (
5056
'compilation_level' in requestedCompileOptions &&
5157
requestedCompileOptions.compilation_level === 'ADVANCED_OPTIMIZATIONS' &&
@@ -68,6 +74,7 @@ export default function closureCompiler(requestedCompileOptions: CompileOptions
6874

6975
const renderChunkTransforms = createChunkTransforms(
7076
context,
77+
requestedCompileOptions,
7178
mangler,
7279
inputOptions,
7380
outputOptions,

src/options.ts

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ import { OutputOptions } from 'rollup';
1919
import { CompileOptions } from 'google-closure-compiler';
2020
import { writeTempFile } from './temp-file';
2121
import { log } from './debug';
22+
import { PluginOptions } from './types';
2223

2324
export const ERROR_WARNINGS_ENABLED_LANGUAGE_OUT_UNSPECIFIED =
2425
'Providing the warning_level=VERBOSE compile option also requires a valid language_out compile option.';
2526
export const ERROR_WARNINGS_ENABLED_LANGUAGE_OUT_INVALID =
2627
'Providing the warning_level=VERBOSE and language_out=NO_TRANSPILE compile options will remove warnings.';
28+
const OPTIONS_TO_REMOVE_FOR_CLOSURE = ['remove_strict_directive'];
2729

2830
/**
2931
* Checks if output format is ESM
@@ -48,6 +50,61 @@ function validateCompileOptions(compileOptions: CompileOptions): void {
4850
}
4951
}
5052

53+
/**
54+
* Normalize the compile options given by the user into something usable.
55+
* @param compileOptions
56+
*/
57+
function normalizeExternOptions(compileOptions: CompileOptions): [Array<string>, CompileOptions] {
58+
validateCompileOptions(compileOptions);
59+
let externs: Array<string> = [];
60+
61+
if ('externs' in compileOptions) {
62+
switch (typeof compileOptions.externs) {
63+
case 'boolean':
64+
externs = [];
65+
break;
66+
case 'string':
67+
externs = [compileOptions.externs as string];
68+
break;
69+
default:
70+
externs = compileOptions.externs as Array<string>;
71+
break;
72+
}
73+
74+
delete compileOptions.externs;
75+
}
76+
77+
if (compileOptions) {
78+
for (const optionToDelete of OPTIONS_TO_REMOVE_FOR_CLOSURE) {
79+
if (optionToDelete in compileOptions) {
80+
// @ts-ignore
81+
delete compileOptions[optionToDelete];
82+
}
83+
}
84+
}
85+
86+
return [externs, compileOptions];
87+
}
88+
89+
/**
90+
* Pluck the PluginOptions from the CompileOptions
91+
* @param compileOptions
92+
*/
93+
export function pluckPluginOptions(compileOptions: CompileOptions): PluginOptions {
94+
const pluginOptions: PluginOptions = {};
95+
if (!compileOptions) {
96+
return pluginOptions;
97+
}
98+
99+
for (const optionToDelete of OPTIONS_TO_REMOVE_FOR_CLOSURE) {
100+
if (optionToDelete in compileOptions) {
101+
// @ts-ignore
102+
pluginOptions[optionToDelete] = compileOptions[optionToDelete];
103+
}
104+
}
105+
return pluginOptions;
106+
}
107+
51108
/**
52109
* Generate default Closure Compiler CompileOptions an author can override if they wish.
53110
* These must be derived from configuration or input sources.
@@ -100,25 +157,7 @@ export default async function(
100157
transforms: Array<ChunkTransform> | null,
101158
): Promise<[CompileOptions, string]> {
102159
const mapFile: string = await writeTempFile('', '', false);
103-
const compileOptions: CompileOptions = { ...incomingCompileOptions };
104-
let externs: Array<string> = [];
105-
106-
validateCompileOptions(compileOptions);
107-
if ('externs' in compileOptions) {
108-
switch (typeof compileOptions.externs) {
109-
case 'boolean':
110-
externs = [];
111-
break;
112-
case 'string':
113-
externs = [compileOptions.externs as string];
114-
break;
115-
default:
116-
externs = compileOptions.externs as Array<string>;
117-
break;
118-
}
119-
120-
delete compileOptions.externs;
121-
}
160+
const [externs, compileOptions] = normalizeExternOptions({ ...incomingCompileOptions });
122161

123162
const options = {
124163
...(await defaults(outputOptions, externs, transforms)),

src/transform.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
import { logTransformChain } from './debug';
18-
import { TransformInterface } from './types';
18+
import { TransformInterface, PluginOptions } from './types';
1919
import { PluginContext, InputOptions, OutputOptions, TransformSourceDescription } from 'rollup';
2020
import { Mangle } from './transformers/mangle';
2121
import * as path from 'path';
@@ -25,18 +25,21 @@ import { createDecodedSourceMap, createExistingRawSourceMap } from './source-map
2525

2626
class Transform implements TransformInterface {
2727
protected context: PluginContext;
28+
protected pluginOptions: PluginOptions;
2829
protected mangler: Mangle;
2930
protected inputOptions: InputOptions;
3031
protected outputOptions: OutputOptions;
3132
public name: string = 'Transform';
3233

3334
constructor(
3435
context: PluginContext,
36+
pluginOptions: PluginOptions,
3537
mangler: Mangle,
3638
inputOptions: InputOptions,
3739
outputOptions: OutputOptions,
3840
) {
3941
this.context = context;
42+
this.pluginOptions = pluginOptions;
4043
this.mangler = mangler;
4144
this.inputOptions = inputOptions;
4245
this.outputOptions = outputOptions;

src/transformers/chunk/strict.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,33 @@
1515
*/
1616

1717
import { ChunkTransform } from '../../transform';
18-
import { Range, TransformInterface } from '../../types';
18+
import { Range, TransformInterface, PluginOptions } from '../../types';
1919
import { isESMFormat } from '../../options';
2020
import MagicString from 'magic-string';
2121
import { walk, parse } from '../../acorn';
2222
import { ExpressionStatement, SimpleLiteral } from 'estree';
2323
import { extname } from 'path';
24+
import { OutputOptions } from 'rollup';
25+
26+
/**
27+
* Determines if Strict Mode should be removed from output.
28+
* @param pluginOptions
29+
* @param outputOptions
30+
* @param path
31+
*/
32+
function shouldRemoveStrictModeDeclarations(
33+
pluginOptions: PluginOptions,
34+
outputOptions: OutputOptions,
35+
path: string | undefined,
36+
): boolean {
37+
if ('remove_strict_directive' in pluginOptions) {
38+
const removeDirective = pluginOptions['remove_strict_directive'];
39+
return removeDirective === undefined || removeDirective === true;
40+
}
41+
42+
const isESMOutput: boolean = !!(path && extname(path) === '.mjs');
43+
return isESMFormat(outputOptions) || isESMOutput;
44+
}
2445

2546
export default class StrictTransform extends ChunkTransform implements TransformInterface {
2647
public name = 'StrictTransform';
@@ -34,7 +55,7 @@ export default class StrictTransform extends ChunkTransform implements Transform
3455
public async post(source: MagicString): Promise<MagicString> {
3556
const { file } = this.outputOptions;
3657

37-
if (isESMFormat(this.outputOptions) || (file && extname(file) === '.mjs')) {
58+
if (shouldRemoveStrictModeDeclarations(this.pluginOptions, this.outputOptions, file)) {
3859
const program = parse(source.toString());
3960

4061
walk.simple(program, {

src/transformers/chunk/transforms.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import StrictTransform from './strict';
3030
import ConstTransform from './const';
3131
import { ChunkTransform, chunkLifecycle } from '../../transform';
3232
import { Mangle } from '../mangle';
33+
import { CompileOptions } from 'google-closure-compiler';
34+
import { pluckPluginOptions } from '../../options';
3335

3436
const TRANSFORMS: Array<typeof ChunkTransform> = [
3537
ConstTransform,
@@ -44,17 +46,24 @@ const TRANSFORMS: Array<typeof ChunkTransform> = [
4446
/**
4547
* Instantiate transform class instances for the plugin invocation.
4648
* @param context Plugin context to bind for each transform instance.
49+
* @param requestedCompileOptions Originally requested compile options from configuration.
50+
* @param mangler Mangle instance used for this transform instance.
4751
* @param inputOptions Rollup input options
4852
* @param outputOptions Rollup output options
4953
* @return Instantiated transform class instances for the given entry point.
5054
*/
51-
export const create = (
55+
export function create(
5256
context: PluginContext,
57+
requestedCompileOptions: CompileOptions,
5358
mangler: Mangle,
5459
inputOptions: InputOptions,
5560
outputOptions: OutputOptions,
56-
): Array<ChunkTransform> =>
57-
TRANSFORMS.map(transform => new transform(context, mangler, inputOptions, outputOptions));
61+
): Array<ChunkTransform> {
62+
const pluginOptions = pluckPluginOptions(requestedCompileOptions);
63+
return TRANSFORMS.map(
64+
transform => new transform(context, pluginOptions, mangler, inputOptions, outputOptions),
65+
);
66+
}
5867

5968
/**
6069
* Run each transform's `preCompilation` phase.

src/transformers/source/transforms.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { SourceTransform, sourceLifecycle } from '../../transform';
1919
// import { ExportTransform } from './exports';
2020
import { Mangle } from '../mangle';
2121
import { PluginContext, InputOptions, OutputOptions, TransformSourceDescription } from 'rollup';
22+
import { CompileOptions } from 'google-closure-compiler';
2223

2324
const TRANSFORMS: Array<typeof SourceTransform> = [];
2425
// Temporarily disabling SourceTransforms, aligning for future release.
@@ -27,17 +28,20 @@ const TRANSFORMS: Array<typeof SourceTransform> = [];
2728
/**
2829
* Instantiate transform class instances for the plugin invocation.
2930
* @param context Plugin context to bind for each transform instance.
31+
* @param requestedCompileOptions Originally requested compile options from configuration.
32+
* @param mangler Mangle instance used for this transform instance.
3033
* @param inputOptions Rollup input options
3134
* @param outputOptions Rollup output options
3235
* @return Instantiated transform class instances for the given entry point.
3336
*/
3437
export const create = (
3538
context: PluginContext,
39+
requestedCompileOptions: CompileOptions,
3640
mangler: Mangle,
3741
inputOptions: InputOptions,
3842
outputOptions: OutputOptions,
3943
): Array<SourceTransform> =>
40-
TRANSFORMS.map(transform => new transform(context, mangler, inputOptions, outputOptions));
44+
TRANSFORMS.map(transform => new transform(context, {}, mangler, inputOptions, outputOptions));
4145

4246
/**
4347
* Run each transform's `transform` lifecycle.

src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,7 @@ export type TransformMethod = (code: string) => Promise<MagicString>;
6262
export interface TransformInterface {
6363
name: string;
6464
}
65+
66+
export interface PluginOptions {
67+
[key: string]: string | boolean;
68+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
(function(a){a.thing=1;return a})({});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict';
2+
3+
export const thing = 1;

test/strict-removal/option.test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright 2018 The AMP HTML Authors. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS-IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {
18+
generator,
19+
DEFAULT_CLOSURE_OPTIONS,
20+
PRETTY_PRINT_CLOSURE_OPTIONS,
21+
ADVANCED_CLOSURE_OPTIONS,
22+
ES5_STRICT_CLOSURE_OPTIONS,
23+
} from '../generator';
24+
25+
const closureOptions = {
26+
default: Object.assign({}, DEFAULT_CLOSURE_OPTIONS.default, {
27+
remove_strict_directive: true,
28+
}),
29+
};
30+
31+
generator('strict-removal', 'option', undefined, ['iife'], closureOptions);

0 commit comments

Comments
 (0)