diff --git a/js/repl/Repl.js b/js/repl/Repl.js index d22b651bab..1ad533f933 100644 --- a/js/repl/Repl.js +++ b/js/repl/Repl.js @@ -5,10 +5,12 @@ import "regenerator-runtime/runtime"; import { cx, css } from "emotion"; import debounce from "lodash.debounce"; import React from "react"; +import JSZip from "jszip"; import { prettySize, compareVersions } from "./Utils"; import ErrorBoundary from "./ErrorBoundary"; import CodeMirrorPanel from "./CodeMirrorPanel"; import ReplOptions from "./ReplOptions"; +import baseBabelConfig from "./baseBabelConfig"; import StorageService from "./StorageService"; import UriUtils from "./UriUtils"; import loadBundle from "./loadBundle"; @@ -244,6 +246,7 @@ class Repl extends React.Component { showOfficialExternalPlugins={state.showOfficialExternalPlugins} loadingExternalPlugins={state.loadingExternalPlugins} onAssumptionsChange={this._onAssumptionsChange} + onDownloadRepl={this._onDownloadRepl} />
{ }); }; + _onDownloadRepl = (e: SyntheticEvent<*>) => { + const { + externalPlugins, + sourceType, + envConfig, + presets, + runtimePolyfillState, + } = this.state; + const zip = new JSZip(); + const folder = zip.folder("my-babel-repl"); + + const packageJson = { + name: "my-babel-repl", + private: true, + version: "1.0.0", + main: "lib/index.js", + scripts: { + babel: "babel src --out-dir lib", + }, + devDependencies: externalPlugins.reduce((prev: Object, plugin) => { + prev[plugin.name] = plugin.version; + return prev; + }, {}), + babel: baseBabelConfig({ + sourceType, + envConfig, + presets, + plugins: externalPlugins.map(plugin => plugin.name), + sourceMap: runtimePolyfillState.isEnabled, + }), + }; + + packageJson.devDependencies["@babel/core"] = "latest"; + packageJson.devDependencies["@babel/cli"] = "latest"; + folder.file("package.json", JSON.stringify(packageJson)); + + const src = folder.folder("src"); + src.file("index.js", this.state.code); + zip.generateAsync({ type: "blob" }).then(v => { + const anchor = document.createElement("a"); + anchor.href = URL.createObjectURL(v); + anchor.download = "my-babel-repl.zip"; + anchor.click(); + document.removeChild(anchor); + }); + e.preventDefault(); + }; + // Debounce compilation since it's expensive. // This also avoids prematurely warning the user about invalid syntax, // eg when in the middle of typing a variable name. diff --git a/js/repl/ReplOptions.js b/js/repl/ReplOptions.js index b228fa9c89..c300926a27 100644 --- a/js/repl/ReplOptions.js +++ b/js/repl/ReplOptions.js @@ -110,6 +110,7 @@ type Props = { runtimePolyfillState: PluginState, loadingExternalPlugins: boolean, onAssumptionsChange: AssumptionsChange, + onDownloadRepl: (e: SyntheticEvent<*>) => void, }; type LinkProps = { @@ -769,9 +770,20 @@ class ExpandedContainer extends Component { />
- {babelVersion && ( -
- {pastVersions.map( @@ -783,8 +795,8 @@ class ExpandedContainer extends Component { ) )} -
- )} + )} +
value.trim()) + .filter(value => value); + } + if (envConfig.isElectronEnabled) { + targets.electron = envConfig.electron; + } + if (envConfig.isBuiltInsEnabled) { + useBuiltIns = !config.evaluate && envConfig.builtIns; + if (envConfig.corejs) { + corejs = envConfig.corejs; + } + } + if (envConfig.isNodeEnabled) { + targets.node = envConfig.node; + } + if (envConfig.isSpecEnabled) { + spec = envConfig.isSpecEnabled; + } + if (envConfig.isLooseEnabled) { + loose = envConfig.isLooseEnabled; + } + if (envConfig.isBugfixesEnabled) { + bugfixes = envConfig.isBugfixesEnabled; + } + + presetEnvOptions = { + targets, + forceAllTransforms, + shippedProposals, + useBuiltIns, + corejs, + spec, + loose, + }; + if (Babel.version && compareVersions(Babel.version, "7.9.0") !== -1) { + (presetEnvOptions: any).bugfixes = bugfixes; + } + } + + return { + sourceMap: config.sourceMap, + assumptions: envConfig?.assumptions ?? {}, + + presets: config.presets.map(preset => { + if (typeof preset !== "string") return preset; + if (preset === "env") { + return ["env", presetEnvOptions]; + } + if (/^stage-[0-2]$/.test(preset)) { + const decoratorsLegacy = presetsOptions.decoratorsLegacy; + const decoratorsBeforeExport = decoratorsLegacy + ? undefined + : presetsOptions.decoratorsBeforeExport; + + return [ + preset, + { + decoratorsLegacy, + decoratorsBeforeExport, + pipelineProposal: presetsOptions.pipelineProposal, + }, + ]; + } + if (preset === "react") { + return [ + "react", + { + runtime: presetsOptions.reactRuntime, + }, + ]; + } + return preset; + }), + plugins: config.plugins, + sourceType: config.sourceType, + }; +} diff --git a/js/repl/compile.js b/js/repl/compile.js index a77babeea5..f688347cce 100644 --- a/js/repl/compile.js +++ b/js/repl/compile.js @@ -1,8 +1,7 @@ // @flow // Globals pre-loaded by Worker -import { compareVersions } from "./Utils"; - +import baseBabelConfig from "./baseBabelConfig"; declare var Babel: any; declare var prettier: any; declare var prettierPlugins: any; @@ -51,115 +50,23 @@ function guessFileExtension(presets: BabelPresets): SupportedFileExtension { } export default function compile(code: string, config: CompileConfig): Return { - const { envConfig, presetsOptions } = config; - let compiled = null; let compileErrorMessage = null; let envPresetDebugInfo = null; let sourceMap = null; - let useBuiltIns = false; - let spec = false; - let loose = false; - let bugfixes = false; - let corejs = "3.6"; const transitions = new Transitions(); const meta = { compiledSize: 0, rawSize: new Blob([code], { type: "text/plain" }).size, }; - - let presetEnvOptions = {}; - - if (envConfig && envConfig.isEnvPresetEnabled) { - const targets = {}; - const { forceAllTransforms, shippedProposals } = envConfig; - - if (envConfig.browsers) { - targets.browsers = envConfig.browsers - .split(",") - .map(value => value.trim()) - .filter(value => value); - } - if (envConfig.isElectronEnabled) { - targets.electron = envConfig.electron; - } - if (envConfig.isBuiltInsEnabled) { - useBuiltIns = !config.evaluate && envConfig.builtIns; - if (envConfig.corejs) { - corejs = envConfig.corejs; - } - } - if (envConfig.isNodeEnabled) { - targets.node = envConfig.node; - } - if (envConfig.isSpecEnabled) { - spec = envConfig.isSpecEnabled; - } - if (envConfig.isLooseEnabled) { - loose = envConfig.isLooseEnabled; - } - if (envConfig.isBugfixesEnabled) { - bugfixes = envConfig.isBugfixesEnabled; - } - - presetEnvOptions = { - targets, - forceAllTransforms, - shippedProposals, - useBuiltIns, - corejs, - spec, - loose, - }; - if (Babel.version && compareVersions(Babel.version, "7.9.0") !== -1) { - (presetEnvOptions: any).bugfixes = bugfixes; - } - } + const babelConfig = baseBabelConfig(config); + babelConfig.babelrc = false; + babelConfig.filename = "repl" + guessFileExtension(config.presets); + babelConfig.wrapPluginVisitorMethod = config.getTransitions + ? transitions.wrapPluginVisitorMethod + : undefined; try { - const babelConfig = { - babelrc: false, - filename: "repl" + guessFileExtension(config.presets), - sourceMap: config.sourceMap, - assumptions: envConfig?.assumptions ?? {}, - - presets: config.presets.map(preset => { - if (typeof preset !== "string") return preset; - if (preset === "env") { - return ["env", presetEnvOptions]; - } - if (/^stage-[0-2]$/.test(preset)) { - const decoratorsLegacy = presetsOptions.decoratorsLegacy; - const decoratorsBeforeExport = decoratorsLegacy - ? undefined - : presetsOptions.decoratorsBeforeExport; - - return [ - preset, - { - decoratorsLegacy, - decoratorsBeforeExport, - pipelineProposal: presetsOptions.pipelineProposal, - }, - ]; - } - if (preset === "react") { - return [ - "react", - { - runtime: presetsOptions.reactRuntime, - }, - ]; - } - return preset; - }), - plugins: config.plugins, - sourceType: config.sourceType, - wrapPluginVisitorMethod: config.getTransitions - ? transitions.wrapPluginVisitorMethod - : undefined, - }; - const transformed = Babel.transform(code, babelConfig); compiled = transformed.code; diff --git a/package.json b/package.json index 8932cb7ed8..915fd16a18 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "codemirror": "5.56.0", "core-js": "^3.0.1", "emotion": "^9.1.3", + "jszip": "^3.7.1", "lodash.camelcase": "^4.3.0", "lodash.debounce": "^4.0.8", "lz-string": "^1.4.4", diff --git a/yarn.lock b/yarn.lock index 8824d379a1..f3a5ab0e74 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2959,6 +2959,7 @@ __metadata: eslint-plugin-react: ^7.21.2 flow-bin: ^0.98.1 husky: ^4.3.0 + jszip: ^3.7.1 lint-staged: ^10.4.0 lodash.camelcase: ^4.3.0 lodash.debounce: ^4.0.8 @@ -7887,6 +7888,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"immediate@npm:~3.0.5": + version: 3.0.6 + resolution: "immediate@npm:3.0.6" + checksum: e86d4d20e2da86f9a4629d9aee9d56dfae7d3a7ace7f569b0538f2ec2570d86b3cbdc52c546d23be2cfd28306670f44676fc833cd96d9506659b8c5ab8202ca4 + languageName: node + linkType: hard + "immer@npm:8.0.1": version: 8.0.1 resolution: "immer@npm:8.0.1" @@ -8917,6 +8925,18 @@ fsevents@~2.3.1: languageName: node linkType: hard +"jszip@npm:^3.7.1": + version: 3.7.1 + resolution: "jszip@npm:3.7.1" + dependencies: + lie: ~3.3.0 + pako: ~1.0.2 + readable-stream: ~2.3.6 + set-immediate-shim: ~1.0.1 + checksum: e5b3a57ef295ac198fbc4b2021906abdeb0192e07ebf05edad9b4941a31dfa6d4f9e994f354f1c8a4cdf4ed13bfdb28293009355f38acd84c258e374412367b6 + languageName: node + linkType: hard + "junk@npm:^3.1.0": version: 3.1.0 resolution: "junk@npm:3.1.0" @@ -9034,6 +9054,15 @@ fsevents@~2.3.1: languageName: node linkType: hard +"lie@npm:~3.3.0": + version: 3.3.0 + resolution: "lie@npm:3.3.0" + dependencies: + immediate: ~3.0.5 + checksum: 6c758fbea382ca251d8f6f32b4066b4c57b4c5d574babc7c6abf7bd20e522a54bcd364dd95d6695103b189a005fcaf9e55d7c58a6c243d4ebdd0cc803898c8bd + languageName: node + linkType: hard + "lines-and-columns@npm:^1.1.6": version: 1.1.6 resolution: "lines-and-columns@npm:1.1.6" @@ -11026,7 +11055,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"pako@npm:~1.0.5": +"pako@npm:~1.0.2, pako@npm:~1.0.5": version: 1.0.11 resolution: "pako@npm:1.0.11" checksum: 71c60150b68220ec52a404f3c39a4ed38f750e42452b88fe0eb2e6b5c98e91f73f706444359b097aca1e6db83ef8fef50b5a9ec100e30a606cda6da8d45e5439 @@ -13492,6 +13521,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"set-immediate-shim@npm:~1.0.1": + version: 1.0.1 + resolution: "set-immediate-shim@npm:1.0.1" + checksum: be64b6800160830fec11aee59f88c7c20f9368510059673488f4373b2d83996337757bd9bf867356120d64762e2e6347272dd83438d452247e6f19be2fac36d2 + languageName: node + linkType: hard + "set-value@npm:^2.0.0, set-value@npm:^2.0.1": version: 2.0.1 resolution: "set-value@npm:2.0.1"