UNPKG

11.2 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
5 * This code may only be used under the BSD style license found at
6 * http://polymer.github.io/LICENSE.txt
7 * The complete set of authors may be found at
8 * http://polymer.github.io/AUTHORS.txt
9 * The complete set of contributors may be found at
10 * http://polymer.github.io/CONTRIBUTORS.txt
11 * Code distributed by Google as part of the polymer project is also
12 * subject to an additional IP rights grant found at
13 * http://polymer.github.io/PATENTS.txt
14 */
15Object.defineProperty(exports, "__esModule", { value: true });
16const babelCore = require("@babel/core");
17const babylon = require("babylon");
18const uuid = require("uuid/v1");
19const babel_plugin_bare_specifiers_1 = require("./babel-plugin-bare-specifiers");
20const babel_plugin_dynamic_import_amd_1 = require("./babel-plugin-dynamic-import-amd");
21const babel_plugin_import_meta_1 = require("./babel-plugin-import-meta");
22const externalJs = require("./external-js");
23// TODO(aomarks) Switch to babel-preset-env. But how do we get just syntax
24// plugins without turning on transformation, for the case where we are
25// minifying but not compiling?
26// Syntax and transform plugins for ES2015. This is roughly equivalent to
27// @babel/preset-es2015, with modules removed and
28// @babel/plugin-transform-classes pinned to v7.0.0-beta.35 to avoid
29// https://github.com/babel/babel/issues/7506 . As mentioned in the bug, native
30// constructors are wrapped with a ES5 'class' which has a constructor that does
31// nothing; however, the custom elements polyfill needs the polyfilled
32// constructor to be called so that it can supply the element being upgraded as
33// the object to use for `this`.
34const babelTransformEs2015 = [
35 require('@babel/plugin-transform-template-literals'),
36 require('@babel/plugin-transform-literals'),
37 require('@babel/plugin-transform-function-name'),
38 require('@babel/plugin-transform-arrow-functions'),
39 require('@babel/plugin-transform-block-scoped-functions'),
40 require('@babel/plugin-transform-classes'),
41 require('@babel/plugin-transform-object-super'),
42 require('@babel/plugin-transform-shorthand-properties'),
43 require('@babel/plugin-transform-duplicate-keys'),
44 require('@babel/plugin-transform-computed-properties'),
45 require('@babel/plugin-transform-for-of'),
46 require('@babel/plugin-transform-sticky-regex'),
47 require('@babel/plugin-transform-unicode-regex'),
48 require('@babel/plugin-transform-spread'),
49 require('@babel/plugin-transform-parameters'),
50 require('@babel/plugin-transform-destructuring'),
51 require('@babel/plugin-transform-block-scoping'),
52 require('@babel/plugin-transform-typeof-symbol'),
53 require('@babel/plugin-transform-instanceof'),
54 [
55 require('@babel/plugin-transform-regenerator'),
56 { async: false, asyncGenerators: false }
57 ],
58];
59const babelTransformEs2016 = [
60 require('@babel/plugin-transform-exponentiation-operator'),
61];
62const babelTransformEs2017 = [
63 require('@babel/plugin-transform-async-to-generator'),
64];
65const babelTransformEs2018 = [
66 require('@babel/plugin-proposal-object-rest-spread'),
67 require('@babel/plugin-proposal-async-generator-functions'),
68];
69// Loading this plugin removes inlined Babel helpers.
70const babelExternalHelpersPlugin = require('@babel/plugin-external-helpers');
71const babelTransformModulesAmd = [
72 babel_plugin_dynamic_import_amd_1.dynamicImportAmd,
73 babel_plugin_import_meta_1.rewriteImportMeta,
74 require('@babel/plugin-transform-modules-amd'),
75];
76// We enumerate syntax plugins that would automatically be loaded by our
77// transform plugins because we need to support the configuration where we
78// minify but don't compile, and don't want Babel to error when it encounters
79// syntax that we support when compiling.
80const babelSyntaxPlugins = [
81 // ES2017 and below syntax plugins are included by default.
82 // ES2018 (partial)
83 require('@babel/plugin-syntax-object-rest-spread'),
84 require('@babel/plugin-syntax-async-generators'),
85 // Future
86 // require('@babel/plugin-syntax-export-extensions'),
87 require('@babel/plugin-syntax-dynamic-import'),
88 require('@babel/plugin-syntax-import-meta'),
89];
90const babelPresetMinify = require('babel-preset-minify')({}, {
91 // Disable this or you get `{ err: 'Couldn\'t find intersection' }` now.
92 // https://github.com/babel/minify/issues/904
93 builtIns: false,
94 // Disable the minify-constant-folding plugin because it has a bug relating
95 // to invalid substitution of constant values into export specifiers:
96 // https://github.com/babel/minify/issues/820
97 evaluate: false,
98 // TODO(aomarks) Find out why we disabled this plugin.
99 simplifyComparisons: false,
100 // Prevent removal of things that babel thinks are unreachable, but sometimes
101 // gets wrong: https://github.com/Polymer/tools/issues/724
102 deadcode: false,
103 // Prevents this `isPure` on null problem from blowing up minification.
104 // https://github.com/babel/minify/issues/790
105 removeUndefined: false,
106 // Disable the simplify plugin because it can eat some statements preceeding
107 // loops. https://github.com/babel/minify/issues/824
108 simplify: false,
109 // This is breaking ES6 output. https://github.com/Polymer/tools/issues/261
110 mangle: false,
111});
112/**
113 * Transform some JavaScript according to the given options.
114 */
115function jsTransform(js, options) {
116 // Even with no transform plugins, parsing and serializing with Babel will
117 // make some minor formatting changes to the code. Skip Babel altogether
118 // if we have no meaningful changes to make.
119 let doBabelTransform = false;
120 // Note that Babel plugins run in this order:
121 // 1) plugins, first to last
122 // 2) presets, last to first
123 const plugins = [...babelSyntaxPlugins];
124 const presets = [];
125 if (options.externalHelpers) {
126 plugins.push(babelExternalHelpersPlugin);
127 }
128 if (options.minify) {
129 doBabelTransform = true;
130 // Minify last, so push first.
131 presets.push(babelPresetMinify);
132 }
133 if (options.compile === true || options.compile === 'es5') {
134 doBabelTransform = true;
135 plugins.push(...babelTransformEs2015);
136 plugins.push(...babelTransformEs2016);
137 plugins.push(...babelTransformEs2017);
138 plugins.push(...babelTransformEs2018);
139 }
140 else if (options.compile === 'es2015') {
141 doBabelTransform = true;
142 plugins.push(...babelTransformEs2016);
143 plugins.push(...babelTransformEs2017);
144 plugins.push(...babelTransformEs2018);
145 }
146 else if (options.compile === 'es2016') {
147 doBabelTransform = true;
148 plugins.push(...babelTransformEs2017);
149 plugins.push(...babelTransformEs2018);
150 }
151 else if (options.compile === 'es2017') {
152 doBabelTransform = true;
153 plugins.push(...babelTransformEs2018);
154 }
155 if (options.moduleResolution === 'node') {
156 if (!options.filePath) {
157 throw new Error('Cannot perform node module resolution without filePath.');
158 }
159 doBabelTransform = true;
160 plugins.push(babel_plugin_bare_specifiers_1.resolveBareSpecifiers(options.filePath, !!options.isComponentRequest, options.packageName, options.componentDir, options.rootDir));
161 }
162 // When the AMD option is "auto", these options will change based on whether
163 // we have a module or not (unless they are already definitely true).
164 let transformModulesToAmd = options.transformModulesToAmd;
165 if (transformModulesToAmd === true) {
166 doBabelTransform = true;
167 }
168 const maybeDoBabelTransform = doBabelTransform || transformModulesToAmd === 'auto';
169 if (maybeDoBabelTransform) {
170 let ast;
171 try {
172 ast = babylon.parse(js, {
173 // TODO(aomarks) Remove any when typings are updated for babylon 7.
174 // tslint:disable-next-line: no-any
175 sourceType: transformModulesToAmd === 'auto' ? 'unambiguous' :
176 'module',
177 plugins: [
178 'asyncGenerators',
179 'dynamicImport',
180 // tslint:disable-next-line: no-any
181 'importMeta',
182 'objectRestSpread',
183 ],
184 });
185 }
186 catch (e) {
187 if (options.softSyntaxError && e.constructor.name === 'SyntaxError') {
188 console.error('ERROR [polymer-build]: failed to parse JavaScript' +
189 (options.filePath ? ` (${options.filePath}):` : ':'), e);
190 return js;
191 }
192 else {
193 throw e;
194 }
195 }
196 if (transformModulesToAmd === 'auto' &&
197 ast.program.sourceType === 'module') {
198 transformModulesToAmd = true;
199 }
200 if (transformModulesToAmd) {
201 doBabelTransform = true;
202 plugins.push(...babelTransformModulesAmd);
203 }
204 if (doBabelTransform) {
205 const result = babelCore.transformFromAst(ast, js, { presets, plugins });
206 if (result.code === undefined) {
207 throw new Error('Babel transform failed: resulting code was undefined.');
208 }
209 js = result.code;
210 if (!options.externalHelpers && options.compile === 'es5' &&
211 js.includes('regeneratorRuntime')) {
212 js = externalJs.getRegeneratorRuntime() + js;
213 }
214 }
215 }
216 js = replaceTemplateObjectNames(js);
217 return js;
218}
219exports.jsTransform = jsTransform;
220/**
221 * Modifies variables names of tagged template literals (`"_templateObject"`)
222 * from a given string so that they're all unique.
223 *
224 * This is needed to workaround a potential naming collision when individually
225 * transpiled scripts are bundled. See #950.
226 */
227function replaceTemplateObjectNames(js) {
228 // Breakdown of regular expression to match "_templateObject" variables
229 //
230 // Pattern | Meaning
231 // -------------------------------------------------------------------
232 // ( | Group1
233 // _templateObject | Match "_templateObject"
234 // \d* | Match 0 or more digits
235 // \b | Match word boundary
236 // ) | End Group1
237 const searchValueRegex = /(_templateObject\d*\b)/g;
238 // The replacement pattern appends an underscore and UUID to the matches:
239 //
240 // Pattern | Meaning
241 // -------------------------------------------------------------------
242 // $1 | Insert matching Group1 (from above)
243 // _ | Insert "_"
244 // ${uniqueId} | Insert previously generated UUID
245 const uniqueId = uuid().replace(/-/g, '');
246 const replaceValue = `$1_${uniqueId}`;
247 // Example output:
248 // _templateObject -> _templateObject_200817b1154811e887be8b38cea68555
249 // _templateObject2 -> _templateObject2_5e44de8015d111e89b203116b5c54903
250 return js.replace(searchValueRegex, replaceValue);
251}
252//# sourceMappingURL=js-transform.js.map
\No newline at end of file