chore: migrate swc (#4966)

* chore: migrate swc

* chore: update script

* chore: update lock file

* chore: update lock file
This commit is contained in:
狒狒神 2021-12-13 17:09:05 +08:00 committed by GitHub
parent ee78096f80
commit 828bdec72e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 786 additions and 5599 deletions

View File

@ -1,112 +0,0 @@
on:
push:
branches:
- 'swc/*'
name: Build @builder/swc native binaries
jobs:
test-native:
name: Unit Test Native Code
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Install
uses: actions-rs/toolchain@v1
with:
toolchain: nightly-2021-11-05
profile: minimal
- run: cd packages/swc && cargo test
build-native:
strategy:
matrix:
os: [ubuntu-18.04, macos-latest, windows-latest]
description: [default]
include:
- os: ubuntu-18.04
target: x86_64-unknown-linux-gnu
name: linux-x64-gnu
- os: windows-latest
target: x86_64-pc-windows-msvc
name: win32-x64-msvc
- os: macos-latest
target: x86_64-apple-darwin
name: darwin-x64
- os: macos-latest
target: aarch64-apple-darwin
name: darwin-arm64
description: m1
name: builder-swc - ${{ matrix.os }} - ${{ matrix.target }} - node@12
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: 12
check-latest: true
- run: npm run setup
- name: Install
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2021-11-05
target: ${{ matrix.target }}
- name: Cache cargo registry
uses: actions/cache@v1
with:
path: ~/.cargo/registry
key: stable-${{ matrix.os }}-node@14-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo index
uses: actions/cache@v1
with:
path: ~/.cargo/git
key: stable-${{ matrix.os }}-node@14-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }}
- name: Cache native binary
id: binary-cache
uses: actions/cache@v2
with:
path: packages/swc/native/**
key: builder-swc-nightly-2021-08-12-${{ matrix.target }}-${{ hashFiles('.github/workflows/build_native.yml', 'packages/swc/**') }}
- name: Cross build aarch64 setup
if: ${{ matrix.target == 'aarch64-apple-darwin' }}
run: |
sudo rm -rf /Library/Developer/CommandLineTools/SDKs/*;
export CC=$(xcrun -f clang);
export CXX=$(xcrun -f clang++);
SYSROOT=$(xcrun --sdk macosx --show-sdk-path);
export CFLAGS="-isysroot $SYSROOT -isystem $SYSROOT";
rustup target install aarch64-apple-darwin;
- name: 'Build'
if: steps.binary-cache.outputs.cache-hit != true
run: yarn build:swc --target ${{ matrix.target }}
env:
MACOSX_DEPLOYMENT_TARGET: '10.13'
working-directory: ./
- name: Upload artifact
uses: actions/upload-artifact@v2.2.4
with:
name: builder-swc-binaries
path: packages/swc/native/builder-swc.${{ matrix.name }}.node
- name: Clear the cargo caches
run: |
cargo install cargo-cache --no-default-features --features ci-autoclean
cargo-cache
commit:
needs: build-native
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@v2.0.10
with:
name: builder-swc-binaries
path: packages/swc/native
- uses: EndBug/add-and-commit@v7
with:
add: 'packages/swc/native --force'
message: 'Build @builder/swc binaries'

View File

@ -23,7 +23,6 @@ jobs:
- run: npm run dependency:check
- run: npm run lint
- run: npm run test
- run: npm run copy:swc
- run: npm run version:check
- run: npm run coverage
env:

2
.gitignore vendored
View File

@ -33,8 +33,6 @@ coverage
# Packages
packages/*/lib/
packages/swc/target/
packages/swc/npm/**/*.node
# temp folder .ice
examples/*/.ice

View File

@ -14,21 +14,18 @@
"watch": "ts-node ./scripts/watch.ts",
"build": "ts-node ./scripts/build.ts",
"generate:dts": "ts-node ./scripts/generate-dts.ts",
"build:swc": "rm -rf packages/swc/native/** && napi build --platform --release --cargo-cwd packages/swc packages/swc/native",
"version": "ts-node ./scripts/tag-version.ts && napi version -p packages/swc/npm -c packages/swc/package.json",
"version": "ts-node ./scripts/tag-version.ts",
"version:check": "ts-node ./scripts/version-check.ts",
"publish": "npm run copy:swc && npm run generate:dts && ts-node ./scripts/publish-package.ts",
"publish:beta": "npm run copy:swc && npm run generate:dts && PUBLISH_TYPE=beta ts-node ./scripts/publishPackageWithDistTag.ts",
"publish:next": "npm run copy:swc && npm run generate:dts && PUBLISH_TYPE=next VERSION_PREFIX=rc ts-node ./scripts/publishPackageWithDistTag.ts",
"publish": "npm run generate:dts && ts-node ./scripts/publish-package.ts",
"publish:beta": "npm run generate:dts && PUBLISH_TYPE=beta ts-node ./scripts/publishPackageWithDistTag.ts",
"publish:next": "npm run generate:dts && PUBLISH_TYPE=next VERSION_PREFIX=rc ts-node ./scripts/publishPackageWithDistTag.ts",
"publish:stable": "npm run generate:dts && PUBLISH_TAG=release-1 ts-node ./scripts/publish-package.ts",
"publish:stable-beta": "npm run generate:dts && PUBLISH_TAG=release-1 ts-node ./scripts/publish-beta-package.ts",
"sync": "ts-node ./scripts/sync.ts",
"rollback": "ts-node ./scripts/rollback.ts",
"owner": "ts-node ./scripts/owner.ts",
"dependency:check": "ts-node ./scripts/dependency-check.ts",
"copy:swc": "ts-node ./scripts/copySwcBinaries.ts",
"clean": "rimraf packages/*/lib",
"clean:swc": "rimraf packages/swc/native/**",
"lint": "eslint --cache --ext .js,.jsx,.ts,.tsx ./",
"lint:fix": "npm run lint -- --fix",
"test": "jest --forceExit --ci",
@ -85,14 +82,5 @@
"@typescript-eslint/parser": "^4.0.0",
"postcss": "^8.3.5",
"eslint-plugin-react": "7.24.0"
},
"napi": {
"name": "builder-swc",
"triples": {
"additional": [
"aarch64-apple-darwin"
],
"defaults": true
}
}
}

View File

@ -1,10 +0,0 @@
target
Cargo.lock
.cargo
.github
npm
.eslintrc
.prettierignore
rustfmt.toml
yarn.lock
*.node

View File

@ -1,13 +0,0 @@
# Changelog
## 0.1.3
- feat: remove multiple ends code
- chore: add rust cases
- fix: transform react config is invalid
## 0.1.2
- feat: add minify binding for @builder/swc
- chore: update swc version
- fix: error with build mode

2531
packages/swc/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,44 +0,0 @@
[package]
edition = "2018"
name = "builder-swc"
version = "0.1.0"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
anyhow = "1.0"
backtrace = "0.3"
chrono = "0.4"
easy-error = "1.0.0"
napi = { version = "1", features = ["serde-json"] }
napi-derive = "1"
path-clean = "0.1"
regex = "1.5"
serde = "1"
serde_json = "1"
swc = "0.81.1"
swc_atoms = "0.2.7"
swc_common = { version = "0.14.2", features = ["concurrent", "sourcemap"] }
swc_css = "0.20.0"
swc_ecmascript = { version = "0.84.1", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] }
swc_ecma_preset_env = "0.63.1"
swc_node_base = "0.5.1"
swc_stylis = "0.17.0"
fxhash = "0.2.1"
retain_mut = "0.1.3"
pathdiff = "0.2.0"
rustc-hash = "1.1.0"
tracing = { version = "0.1.28", features = ["release_max_level_off"] }
lazy_static = "1.4.0"
[dev-dependencies]
swc_ecma_transforms_testing = "0.43.1"
testing = "0.15.1"
walkdir = "2.3.2"
[build-dependencies]
napi-build = "1"
[profile.release]
lto = true

View File

@ -1,5 +0,0 @@
extern crate napi_build;
fn main() {
napi_build::setup();
}

View File

@ -1 +0,0 @@
incremental = false

View File

@ -1,92 +0,0 @@
/* eslint-disable global-require */
/* eslint-disable no-restricted-syntax */
/* eslint-disable import/no-dynamic-require */
import * as path from 'path';
import * as fs from 'fs';
import { platform, arch } from 'os';
import { platformArchTriples } from '@napi-rs/triples';
import { Options, JsMinifyOptions, Output, Binding } from './types';
const ArchName = arch();
const PlatformName = platform();
/**
* __dirname means load native addon from current dir
* 'swc' is the name of native addon
* the second arguments was decided by `napi.name` field in `package.json`
* the third arguments was decided by `name` field in `package.json`
* `loadBinding` helper will load `swc.[PLATFORM].node` from `__dirname` first
* If failed to load addon, it will fallback to load from `swc-[PLATFORM]`
*/
const bindings: Binding = loadBinding();
function loadBinding() {
const triples = platformArchTriples[PlatformName][ArchName];
for (const triple of triples) {
const localFilePath = path.join(
__dirname,
'../native',
`builder-swc.${triple.platformArchABI}.node`
);
if (fs.existsSync(localFilePath)) {
console.log('Load local native module.');
return require(localFilePath);
}
try {
return require(`@builder/swc-${triple.platformArchABI}`);
// eslint-disable-next-line no-empty
} catch (e) {}
}
throw new Error('Cannot find target @builder/swc native module!');
}
async function transform(src: string, options: Options): Promise<Output> {
options = options || {};
if (options?.jsc?.parser) {
options.jsc.parser.syntax = options.jsc.parser.syntax ?? 'ecmascript';
}
return bindings.transform(
src,
false,
toBuffer(options)
);
}
function transformSync(src: string, options: Options): Output {
options = options || {};
if (options?.jsc?.parser) {
options.jsc.parser.syntax = options.jsc.parser.syntax ?? 'ecmascript';
}
return bindings.transformSync(
src,
false,
toBuffer(options)
);
}
function toBuffer(t) {
return Buffer.from(JSON.stringify(t));
}
async function minify(src: string, opts: JsMinifyOptions): Promise<Output> {
return bindings.minify(toBuffer(src), toBuffer(opts ?? {}));
}
function minifySync(src: string, opts: JsMinifyOptions): Output {
return bindings.minifySync(toBuffer(src), toBuffer(opts ?? {}));
}
export * from './types';
export {
transform,
transformSync,
minify,
minifySync,
};

View File

@ -1,758 +0,0 @@
/* eslint-disable @typescript-eslint/no-empty-interface */
/* eslint-disable camelcase */
export interface Binding {
transformSync: (src: string, isModule: boolean, options: Buffer) => Output;
transform: (src: string, isModule: boolean, options: Buffer) => Promise<Output>;
minifySync: (src: Buffer, options: Buffer) => Output;
minify: (src: Buffer, options: Buffer) => Promise<Output>;
}
export type TerserEcmaVersion = 5 | 2015 | 2016 | string | number;
export interface JsMinifyOptions {
compress?: TerserCompressOptions | boolean,
mangle?: TerserMangleOptions | boolean,
ecma?: TerserEcmaVersion,
keep_classnames?: boolean,
keep_fnames?: boolean,
module?: boolean,
safari10?: boolean
toplevel?: boolean
sourceMap?: boolean
outputPath?: string
inlineSourcesContent?: boolean
}
export interface TerserCompressOptions {
arguments?: boolean,
arrows?: boolean,
booleans?: boolean,
booleans_as_integers?: boolean,
collapse_vars?: boolean,
comparisons?: boolean,
computed_props?: boolean,
conditionals?: boolean,
dead_code?: boolean,
defaults?: boolean,
directives?: boolean,
drop_console?: boolean,
drop_debugger?: boolean,
ecma?: TerserEcmaVersion,
evaluate?: boolean,
expression?: boolean,
global_defs?: any,
hoist_funs?: boolean,
hoist_props?: boolean,
hoist_vars?: boolean,
ie8?: boolean,
if_return?: boolean,
inline?: 0 | 1 | 2 | 3
join_vars?: boolean,
keep_classnames?: boolean,
keep_fargs?: boolean,
keep_fnames?: boolean,
keep_infinity?: boolean,
loops?: boolean,
// module : false,
negate_iife?: boolean,
passes?: number,
properties?: boolean,
pure_getters?: any,
pure_funcs?: string[],
reduce_funcs?: boolean,
reduce_vars?: boolean,
sequences?: any,
side_effects?: boolean,
switches?: boolean,
top_retain?: any,
toplevel?: any,
typeofs?: boolean,
unsafe_passes?: boolean,
unsafe_arrows?: boolean,
unsafe_comps?: boolean,
unsafe_function?: boolean,
unsafe_math?: boolean,
unsafe_symbols?: boolean,
unsafe_methods?: boolean,
unsafe_proto?: boolean,
unsafe_regexp?: boolean,
unsafe_undefined?: boolean,
unused?: boolean,
module?: boolean,
}
export interface TerserMangleOptions {
props?: TerserManglePropertiesOptions,
top_level?: boolean,
keep_class_names?: boolean,
keep_fn_names?: boolean,
keep_private_props?: boolean,
ie8?: boolean,
safari10?: boolean,
}
export interface TerserManglePropertiesOptions {
}
/**
* Programmatic options.
*/
export interface Options extends Config {
/**
* If true, a file is parsed as a script instead of module.
*/
script?: boolean;
/**
* The working directory that all paths in the programmatic
* options will be resolved relative to.
*
* Defaults to `process.cwd()`.
*/
cwd?: string;
caller?: CallerOptions;
/** The filename associated with the code currently being compiled,
* if there is one. The filename is optional, but not all of Swc's
* functionality is available when the filename is unknown, because a
* subset of options rely on the filename for their functionality.
*
* The three primary cases users could run into are:
*
* - The filename is exposed to plugins. Some plugins may require the
* presence of the filename.
* - Options like "test", "exclude", and "ignore" require the filename
* for string/RegExp matching.
* - .swcrc files are loaded relative to the file being compiled.
* If this option is omitted, Swc will behave as if swcrc: false has been set.
*/
filename?: string;
/**
* The initial path that will be processed based on the "rootMode" to
* determine the conceptual root folder for the current Swc project.
* This is used in two primary cases:
*
* - The base directory when checking for the default "configFile" value
* - The default value for "swcrcRoots".
*
* Defaults to `opts.cwd`
*/
root?: string;
/**
* This option, combined with the "root" value, defines how Swc chooses
* its project root. The different modes define different ways that Swc
* can process the "root" value to get the final project root.
*
* "root" - Passes the "root" value through as unchanged.
* "upward" - Walks upward from the "root" directory, looking for a directory
* containinga swc.config.js file, and throws an error if a swc.config.js
* is not found.
* "upward-optional" - Walk upward from the "root" directory, looking for
* a directory containing a swc.config.js file, and falls back to "root"
* if a swc.config.js is not found.
*
*
* "root" is the default mode because it avoids the risk that Swc
* will accidentally load a swc.config.js that is entirely outside
* of the current project folder. If you use "upward-optional",
* be aware that it will walk up the directory structure all the
* way to the filesystem root, and it is always possible that someone
* will have a forgotten swc.config.js in their home directory,
* which could cause unexpected errors in your builds.
*
*
* Users with monorepo project structures that run builds/tests on a
* per-package basis may well want to use "upward" since monorepos
* often have a swc.config.js in the project root. Running Swc
* in a monorepo subdirectory without "upward", will cause Swc
* to skip loading any swc.config.js files in the project root,
* which can lead to unexpected errors and compilation failure.
*/
rootMode?: 'root' | 'upward' | 'upward-optional';
/**
* The current active environment used during configuration loading.
* This value is used as the key when resolving "env" configs,
* and is also available inside configuration functions, plugins,
* and presets, via the api.env() function.
*
* Defaults to `process.env.SWC_ENV || process.env.NODE_ENV || "development"`
*/
envName?: string;
/**
* Defaults to searching for a default `.swcrc` file, but can
* be passed the path of any JS or JSON5 config file.
*
*
* NOTE: This option does not affect loading of .swcrc files,
* so while it may be tempting to do configFile: "./foo/.swcrc",
* it is not recommended. If the given .swcrc is loaded via the
* standard file-relative logic, you'll end up loading the same
* config file twice, merging it with itself. If you are linking
* a specific config file, it is recommended to stick with a
* naming scheme that is independent of the "swcrc" name.
*
* Defaults to `path.resolve(opts.root, ".swcrc")`
*/
configFile?: string | boolean;
/**
* true will enable searching for configuration files relative to the "filename" provided to Swc.
*
* A swcrc value passed in the programmatic options will override one set within a configuration file.
*
* Note: .swcrc files are only loaded if the current "filename" is inside of
* a package that matches one of the "swcrcRoots" packages.
*
*
* Defaults to true as long as the filename option has been specificed
*/
swcrc?: boolean;
/**
* By default, Babel will only search for .babelrc files within the "root" package
* because otherwise Babel cannot know if a given .babelrc is meant to be loaded,
* or if it's "plugins" and "presets" have even been installed, since the file
* being compiled could be inside node_modules, or have been symlinked into the project.
*
*
* This option allows users to provide a list of other packages that should be
* considered "root" packages when considering whether to load .babelrc files.
*
*
* For example, a monorepo setup that wishes to allow individual packages
* to have their own configs might want to do
*
*
*
* Defaults to `opts.root`
*/
swcrcRoots?: boolean | MatchPattern | MatchPattern[];
/**
* `true` will attempt to load an input sourcemap from the file itself, if it
* contains a //# sourceMappingURL=... comment. If no map is found, or the
* map fails to load and parse, it will be silently discarded.
*
* If an object is provided, it will be treated as the source map object itself.
*
* Defaults to `true`.
*/
inputSourceMap?: boolean | string;
/**
* The name to use for the file inside the source map object.
*
* Defaults to `path.basename(opts.filenameRelative)` when available, or `"unknown"`.
*/
sourceFileName?: string;
/**
* The sourceRoot fields to set in the generated source map, if one is desired.
*/
sourceRoot?: string;
plugin?: Plugin;
isModule?: boolean;
/**
* Destination path. Note that this value is used only to fix source path
* of source map files and swc does not write output to this path.
*/
outputPath?: string;
keepPlatform ?: string;
}
export interface CallerOptions {
name: string;
[key: string]: any;
}
export type Swcrc = Config | Config[];
/**
* .swcrc
*/
export interface Config {
/**
* Note: The type is string beacuse it follow rust's regex syntax.
*/
test?: string | string[];
/**
* Note: The type is string beacuse it follow rust's regex syntax.
*/
exclude?: string | string[];
env?: EnvConfig;
jsc?: JscConfig;
module?: ModuleConfig;
minify?: boolean;
/**
* - true to generate a sourcemap for the code and include it in the result object.
* - "inline" to generate a sourcemap and append it as a data URL to the end of the code, but not include it in the result object.
*
* `swc-cli` overloads some of these to also affect how maps are written to disk:
*
* - true will write the map to a .map file on disk
* - "inline" will write the file directly, so it will have a data: containing the map
* - Note: These options are bit weird, so it may make the most sense to just use true
* and handle the rest in your own code, depending on your use case.
*/
sourceMaps?: boolean | 'inline';
inlineSourcesContent?: boolean
}
/**
* Configuration ported from babel-preset-env
*/
export interface EnvConfig {
mode?: 'usage' | 'entry';
debug?: boolean;
dynamicImport?: boolean;
loose?: boolean;
/// Skipped es features.
///
/// e.g.)
/// - `core-js/modules/foo`
skip?: string[];
include?: string[];
exclude?: string[];
/**
* The version of the used core js.
*
*/
coreJs?: string;
targets?: any;
path?: string;
shippedProposals?: boolean;
/**
* Enable all trnasforms
*/
forceAllTransforms?: boolean;
}
export interface JscConfig {
loose?: boolean;
/**
* Defaults to EsParserConfig
*/
parser?: ParserConfig;
transform?: TransformConfig;
/**
* Use `@swc/helpers` instead of inline helpers.
*/
externalHelpers?: boolean;
/**
* Defaults to `es3` (which enableds **all** pass).
*/
target?: JscTarget;
/**
* Keep class names.
*/
keepClassNames?: boolean
experimetal?: {
optimizeHygiene?: boolean
},
paths?: {
[from: string]: [string]
},
minify?: JsMinifyOptions
}
export type JscTarget =
| 'es3'
| 'es5'
| 'es2015'
| 'es2016'
| 'es2017'
| 'es2018'
| 'es2019'
| 'es2020'
| 'es2021';
export type ParserConfig = TsParserConfig | EsParserConfig;
export interface TsParserConfig {
syntax: 'typescript';
/**
* Defaults to `false`.
*/
tsx?: boolean;
/**
* Defaults to `false`.
*/
decorators?: boolean;
/**
* Defaults to `false`
*/
dynamicImport?: boolean;
}
export interface EsParserConfig {
syntax: 'ecmascript';
/**
* Defaults to false.
*/
jsx?: boolean;
/**
* @deprecated Always true because it's in ecmascript spec.
*/
numericSeparator?: boolean;
/**
* @deprecated Always true because it's in ecmascript spec.
*/
classPrivateProperty?: boolean;
/**
* @deprecated Always true because it's in ecmascript spec.
*/
privateMethod?: boolean;
/**
* @deprecated Always true because it's in ecmascript spec.
*/
classProperty?: boolean;
/**
* Defaults to `false`
*/
functionBind?: boolean;
/**
* Defaults to `false`
*/
decorators?: boolean;
/**
* Defaults to `false`
*/
decoratorsBeforeExport?: boolean;
/**
* Defaults to `false`
*/
exportDefaultFrom?: boolean;
/**
* @deprecated Always true because it's in ecmascript spec.
*/
exportNamespaceFrom?: boolean;
/**
* @deprecated Always true because it's in ecmascript spec.
*/
dynamicImport?: boolean;
/**
* @deprecated Always true because it's in ecmascript spec.
*/
nullishCoalescing?: boolean;
/**
* @deprecated Always true because it's in ecmascript spec.
*/
optionalChaining?: boolean;
/**
* @deprecated Always true because it's in ecmascript spec.
*/
importMeta?: boolean;
/**
* @deprecated Always true because it's in ecmascript spec.
*/
topLevelAwait?: boolean;
/**
* Defaults to `false`
*/
importAssertions?: boolean;
}
/**
* Options for trasnform.
*/
export interface TransformConfig {
/**
* Effective only if `syntax` supports ƒ.
*/
react?: ReactConfig;
constModules?: ConstModulesConfig;
/**
* Defaults to null, which skips optimizer pass.
*/
optimizer?: OptimizerConfig;
/**
* https://swc.rs/docs/configuring-swc.html#jsctransformlegacydecorator
*/
legacyDecorator?: boolean;
/**
* https://swc.rs/docs/configuring-swc.html#jsctransformdecoratormetadata
*/
decoratorMetadata?: boolean;
}
export interface ReactConfig {
/**
* Replace the function used when compiling JSX expressions.
*
* Defaults to `React.createElement`.
*/
pragma?: string;
/**
* Replace the component used when compiling JSX fragments.
*
* Defaults to `React.Fragment`
*/
pragmaFrag?: string;
/**
* Toggles whether or not to throw an error if a XML namespaced tag name is used. For example:
* `<f:image />`
*
* Though the JSX spec allows this, it is disabled by default since React's
* JSX does not currently have support for it.
*
*/
throwIfNamespace?: boolean;
/**
* Toggles plugins that aid in development, such as @swc/plugin-transform-react-jsx-self
* and @swc/plugin-transform-react-jsx-source.
*
* Defaults to `false`,
*
*/
development?: boolean;
/**
* Use `Object.assign()` instead of `_extends`. Defaults to false.
*/
useBuiltins?: boolean;
/**
* Enable fast refresh feature for React app
*/
refresh?: boolean;
/**
* jsx runtime
*/
runtime?: 'automatic' | 'classic'
/**
* Declares the module specifier to be used for importing the `jsx` and `jsxs` factory functions when using `runtime` 'automatic'
*/
importSource?: string
}
/**
* - `import { DEBUG } from '@ember/env-flags';`
* - `import { FEATURE_A, FEATURE_B } from '@ember/features';`
*
* See: https://github.com/swc-project/swc/issues/18#issuecomment-466272558
*/
export interface ConstModulesConfig {
globals?: {
[module: string]: {
[name: string]: string;
};
};
}
/// https://swc.rs/docs/configuring-swc.html#jsctransformoptimizerjsonify
export interface OptimizerConfig {
/// https://swc.rs/docs/configuring-swc#jsctransformoptimizer
globals?: GlobalPassOption;
jsonify?: { minCost: number };
simplify?: boolean;
}
/**
* Options for inline-global pass.
*/
export interface GlobalPassOption {
/**
* Global variables.
*
* e.g. `{ __DEBUG__: true }`
*/
vars?: { [key: string]: string };
/**
* Name of environment variables to inline.
*
* Defaults to `["NODE_ENV", "SWC_ENV"]`
*/
envs?: string[];
}
export type ModuleConfig = CommonJsConfig | UmdConfig | AmdConfig;
export interface BaseModuleConfig {
/**
* By default, when using exports with babel a non-enumerable `__esModule`
* property is exported. In some cases this property is used to determine
* if the import is the default export or if it contains the default export.
*
* In order to prevent the __esModule property from being exported, you
* can set the strict option to true.
*
* Defaults to `false`.
*/
strict?: boolean;
/**
* Emits 'use strict' directive.
*
* Defaults to `true`.
*/
strictMode?: boolean;
/**
* Changes Babel's compiled import statements to be lazily evaluated when their imported bindings are used for the first time.
*
* This can improve initial load time of your module because evaluating dependencies up
* front is sometimes entirely un-necessary. This is especially the case when implementing
* a library module.
*
*
* The value of `lazy` has a few possible effects:
*
* - `false` - No lazy initialization of any imported module.
* - `true` - Do not lazy-initialize local `./foo` imports, but lazy-init `foo` dependencies.
*
* Local paths are much more likely to have circular dependencies, which may break if loaded lazily,
* so they are not lazy by default, whereas dependencies between independent modules are rarely cyclical.
*
* - `Array<string>` - Lazy-initialize all imports with source matching one of the given strings.
*
* -----
*
* The two cases where imports can never be lazy are:
*
* - `import "foo";`
*
* Side-effect imports are automatically non-lazy since their very existence means
* that there is no binding to later kick off initialization.
*
* - `export * from "foo"`
*
* Re-exporting all names requires up-front execution because otherwise there is no
* way to know what names need to be exported.
*
* Defaults to `false`.
*/
lazy?: boolean | string[];
/**
* By default, when using exports with swc a non-enumerable __esModule property is exported.
* This property is then used to determine if the import is the default export or if
* it contains the default export.
*
* In cases where the auto-unwrapping of default is not needed, you can set the noInterop option
* to true to avoid the usage of the interopRequireDefault helper (shown in inline form above).
*
* Defaults to `false`.
*/
noInterop?: boolean;
}
export interface CommonJsConfig extends BaseModuleConfig {
type: 'commonjs';
}
export interface UmdConfig extends BaseModuleConfig {
type: 'umd';
globals?: { [key: string]: string };
}
export interface AmdConfig extends BaseModuleConfig {
type: 'amd';
moduleId?: string;
}
export interface Output {
/**
* Transformed code
*/
code: string;
/**
* Sourcemap (**not** base64 encoded)
*/
map?: string;
}
export interface MatchPattern { }

View File

@ -1,3 +0,0 @@
# `swc-darwin-arm64`
This is the **aarch64-apple-darwin** binary for `swc`

View File

@ -1,18 +0,0 @@
{
"name": "@builder/swc-darwin-arm64",
"version": "0.1.3",
"os": [
"darwin"
],
"cpu": [
"arm64"
],
"main": "builder-swc.darwin-arm64.node",
"files": [
"builder-swc.darwin-arm64.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@ -1,3 +0,0 @@
# `swc-darwin-x64`
This is the **x86_64-apple-darwin** binary for `swc`

View File

@ -1,18 +0,0 @@
{
"name": "@builder/swc-darwin-x64",
"version": "0.1.3",
"os": [
"darwin"
],
"cpu": [
"x64"
],
"main": "builder-swc.darwin-x64.node",
"files": [
"builder-swc.darwin-x64.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@ -1,3 +0,0 @@
# `swc-linux-x64-gnu`
This is the **x86_64-unknown-linux-gnu** binary for `swc`

View File

@ -1,18 +0,0 @@
{
"name": "@builder/swc-linux-x64-gnu",
"version": "0.1.3",
"os": [
"linux"
],
"cpu": [
"x64"
],
"main": "builder-swc.linux-x64-gnu.node",
"files": [
"builder-swc.linux-x64-gnu.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@ -1,3 +0,0 @@
# `swc-win32-x64-msvc`
This is the **x86_64-pc-windows-msvc** binary for `swc`

View File

@ -1,18 +0,0 @@
{
"name": "@builder/swc-win32-x64-msvc",
"version": "0.1.3",
"os": [
"win32"
],
"cpu": [
"x64"
],
"main": "builder-swc.win32-x64-msvc.node",
"files": [
"builder-swc.win32-x64-msvc.node"
],
"license": "MIT",
"engines": {
"node": ">= 10"
}
}

View File

@ -1,27 +0,0 @@
{
"name": "@builder/swc",
"version": "0.1.3",
"main": "lib/index.js",
"napi": {
"name": "builder-swc",
"triples": {
"additional": [
"aarch64-apple-darwin"
],
"defaults": true
}
},
"license": "MIT",
"dependencies": {
"@napi-rs/triples": "^1.0.3"
},
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@builder/swc-darwin-x64": "^0.1.0",
"@builder/swc-linux-x64-gnu": "^0.1.0",
"@builder/swc-darwin-arm64": "^0.1.0",
"@builder/swc-win32-x64-msvc": "^0.1.0"
}
}

View File

@ -1 +0,0 @@
nightly-2021-11-05

View File

@ -1,140 +0,0 @@
use crate::lazy_static;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use swc_common::DUMMY_SP;
use swc_ecmascript::ast::{
BindingIdent, Bool, Decl, Expr, Ident, ImportNamedSpecifier, ImportSpecifier, Lit, ModuleDecl,
ModuleItem, Pat, Stmt, VarDecl, VarDeclKind, VarDeclarator,
};
use swc_ecmascript::visit::Fold;
#[derive(Debug, Deserialize, Default, Clone)]
pub struct KeepPlatformPatcher {
pub platform: String,
}
/// Configuration related to source map generated by swc.
#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum KeepPlatformConfig {
Bool(bool),
KeepPlatform(String),
}
impl Default for KeepPlatformConfig {
fn default() -> Self {
KeepPlatformConfig::Bool(false)
}
}
pub fn keep_platform(options: KeepPlatformConfig) -> impl Fold {
let platform: String = match options {
KeepPlatformConfig::KeepPlatform(platform) => platform,
_ => "".to_string(),
};
KeepPlatformPatcher { platform: platform }
}
// platform maps
lazy_static! {
static ref PLATFORM_MAP: HashMap<String, Vec<String>> = HashMap::from([
("web".to_string(), vec!["isWeb".to_string()]),
("node".to_string(), vec!["isNode".to_string()]),
("weex".to_string(), vec!["isWeex".to_string()]),
(
"kraken".to_string(),
vec!["isKraken".to_string(), "isWeb".to_string()]
),
(
"wechat-miniprogram".to_string(),
vec![
"isWeChatMiniProgram".to_string(),
"isWeChatMiniprogram".to_string()
]
),
("miniapp".to_string(), vec!["isMiniApp".to_string()]),
(
"bytedance-microapp".to_string(),
vec!["isByteDanceMicroApp".to_string()]
),
(
"kuaishou-miniprogram".to_string(),
vec!["isKuaiShouMiniProgram".to_string()]
),
(
"baidu-smartprogram".to_string(),
vec!["isBaiduSmartProgram".to_string()]
),
]);
}
impl Fold for KeepPlatformPatcher {
fn fold_module_items(&mut self, items: Vec<ModuleItem>) -> Vec<ModuleItem> {
// Get platform flag, such as ["isWeb"]
let platform_flags: Vec<String> = match PLATFORM_MAP.get(&self.platform.to_string()) {
Some(flags) => flags.to_vec(),
None => vec![],
};
// Collect top-level expression
let mut new_module_items: Vec<ModuleItem> = vec![];
// Save isWeb/isWeex into env_variables
let mut env_variables: Vec<&Ident> = vec![];
for module_item in items.iter() {
match module_item {
ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) => {
if &import_decl.src.value == "universal-env" {
for specifier in import_decl.specifiers.iter() {
match specifier {
ImportSpecifier::Named(named) => {
let ImportNamedSpecifier {
local,
span: _,
imported: _,
is_type_only: _,
} = named;
env_variables.push(local);
}
_ => {}
}
}
} else {
new_module_items.push(ModuleItem::ModuleDecl(ModuleDecl::Import(
import_decl.clone(),
)))
}
}
_ => new_module_items.push(module_item.clone()),
}
}
// If it exist env variables, we need insert declare expression
if env_variables.len() > 0 {
for env_variable in env_variables {
let decs: Vec<VarDeclarator> = vec![VarDeclarator {
span: DUMMY_SP,
definite: false,
name: Pat::Ident(BindingIdent {
id: env_variable.clone(),
type_ann: Default::default(),
}),
// Init value, such as var isWeb = true
init: Option::Some(Box::new(Expr::Lit(Lit::Bool(Bool {
value: platform_flags.contains(&env_variable.sym.to_string()),
span: Default::default(),
})))),
}];
new_module_items.insert(
0,
ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls: decs,
}))),
);
}
}
return new_module_items;
}
}

View File

@ -1,90 +0,0 @@
#![recursion_limit = "2048"]
//#![deny(clippy::all)]
#[macro_use]
extern crate napi_derive;
extern crate lazy_static;
/// Explicit extern crate to use allocator.
extern crate swc_node_base;
use backtrace::Backtrace;
use lazy_static::lazy_static;
use napi::{CallContext, Env, JsObject, JsUndefined};
use serde::Deserialize;
use std::{env, panic::set_hook, sync::Arc};
use swc::{Compiler, TransformOutput};
use swc_common::{self, chain, pass::Optional, sync::Lazy, FileName, FilePathMapping, SourceMap};
use swc_ecmascript::transforms::pass::noop;
use swc_ecmascript::visit::Fold;
use crate::keep_platform::{keep_platform, KeepPlatformConfig};
pub mod keep_platform;
pub mod minify;
pub mod transform;
mod util;
#[derive(Debug, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct TransformOptions {
#[serde(flatten)]
pub swc: swc::config::Options,
#[serde(default)]
pub keep_platform: KeepPlatformConfig,
}
pub fn custom_before_pass(name: &FileName, options: &TransformOptions) -> impl Fold {
let mut keep_platform_config = KeepPlatformConfig::Bool(false);
let enable_keep_platform: bool = match options.keep_platform.clone() {
KeepPlatformConfig::KeepPlatform(platform) => {
keep_platform_config = KeepPlatformConfig::KeepPlatform(platform);
true
}
KeepPlatformConfig::Bool(val) => val,
};
// custom before pass
chain!(
Optional::new(keep_platform(keep_platform_config), enable_keep_platform),
noop()
)
}
static COMPILER: Lazy<Arc<Compiler>> = Lazy::new(|| {
let cm = Arc::new(SourceMap::new(FilePathMapping::empty()));
Arc::new(Compiler::new(cm.clone()))
});
#[module_exports]
fn init(mut exports: JsObject) -> napi::Result<()> {
if cfg!(debug_assertions) || env::var("SWC_DEBUG").unwrap_or_default() == "1" {
set_hook(Box::new(|panic_info| {
let backtrace = Backtrace::new();
println!("Panic: {:?}\nBacktrace: {:?}", panic_info, backtrace);
}));
}
exports.create_named_method("transform", transform::transform)?;
exports.create_named_method("transformSync", transform::transform_sync)?;
exports.create_named_method("minify", minify::minify)?;
exports.create_named_method("minifySync", minify::minify_sync)?;
Ok(())
}
fn get_compiler(_ctx: &CallContext) -> Arc<Compiler> {
COMPILER.clone()
}
#[js_function]
fn construct_compiler(ctx: CallContext) -> napi::Result<JsUndefined> {
// TODO: Assign swc::Compiler
ctx.env.get_undefined()
}
pub fn complete_output(env: &Env, output: TransformOutput) -> napi::Result<JsObject> {
env.to_js_value(&output)?.coerce_to_object()
}
pub type ArcCompiler = Arc<Compiler>;

View File

@ -1,90 +0,0 @@
use crate::{
complete_output, get_compiler,
util::{CtxtExt, MapErr},
};
use fxhash::FxHashMap;
use napi::{CallContext, JsObject, Task};
use serde::Deserialize;
use std::sync::Arc;
use swc::{try_with_handler, TransformOutput};
use swc_common::{sync::Lrc, FileName, SourceFile, SourceMap};
struct MinifyTask {
c: Arc<swc::Compiler>,
code: MinifyTarget,
opts: swc::config::JsMinifyOptions,
}
#[derive(Deserialize)]
#[serde(untagged)]
enum MinifyTarget {
/// Code to minify.
Single(String),
/// `{ filename: code }`
Map(FxHashMap<String, String>),
}
impl MinifyTarget {
fn to_file(&self, cm: Lrc<SourceMap>) -> Lrc<SourceFile> {
match self {
MinifyTarget::Single(code) => cm.new_source_file(FileName::Anon, code.clone()),
MinifyTarget::Map(codes) => {
assert_eq!(
codes.len(),
1,
"swc.minify does not support concatenating multiple files yet"
);
let (filename, code) = codes.iter().next().unwrap();
cm.new_source_file(FileName::Real(filename.clone().into()), code.clone())
}
}
}
}
impl Task for MinifyTask {
type Output = TransformOutput;
type JsValue = JsObject;
fn compute(&mut self) -> napi::Result<Self::Output> {
try_with_handler(self.c.cm.clone(), true, |handler| {
let fm = self.code.to_file(self.c.cm.clone());
self.c.minify(fm, &handler, &self.opts)
})
.convert_err()
}
fn resolve(self, env: napi::Env, output: Self::Output) -> napi::Result<Self::JsValue> {
complete_output(&env, output)
}
}
#[js_function(2)]
pub fn minify(cx: CallContext) -> napi::Result<JsObject> {
let code = cx.get_deserialized(0)?;
let opts = cx.get_deserialized(1)?;
let c = get_compiler(&cx);
let task = MinifyTask { c, code, opts };
cx.env.spawn(task).map(|t| t.promise_object())
}
#[js_function(2)]
pub fn minify_sync(cx: CallContext) -> napi::Result<JsObject> {
let code: MinifyTarget = cx.get_deserialized(0)?;
let opts = cx.get_deserialized(1)?;
let c = get_compiler(&cx);
let fm = code.to_file(c.cm.clone());
let output = try_with_handler(c.cm.clone(), true, |handler| c.minify(fm, &handler, &opts))
.convert_err()?;
complete_output(&cx.env, output)
}

View File

@ -1,172 +0,0 @@
use crate::{
complete_output, custom_before_pass, get_compiler,
util::{deserialize_json, CtxtExt, MapErr},
TransformOptions,
};
use anyhow::{anyhow, Context as _, Error};
use napi::{CallContext, Env, JsBoolean, JsObject, JsString, Status, Task};
use std::{
panic::{catch_unwind, AssertUnwindSafe},
sync::Arc,
};
use swc::{try_with_handler, Compiler, TransformOutput};
use swc_common::{FileName, SourceFile};
use swc_ecmascript::ast::Program;
use swc_ecmascript::transforms::pass::noop;
/// Input to transform
#[derive(Debug)]
pub enum Input {
/// Raw source code.
Source { src: String },
}
pub struct TransformTask {
pub c: Arc<Compiler>,
pub input: Input,
pub options: String,
}
impl Task for TransformTask {
type Output = TransformOutput;
type JsValue = JsObject;
fn compute(&mut self) -> napi::Result<Self::Output> {
let res = catch_unwind(AssertUnwindSafe(|| {
try_with_handler(self.c.cm.clone(), true, |handler| {
self.c.run(|| match &self.input {
Input::Source { src } => {
let options: TransformOptions = deserialize_json(&self.options)?;
let filename = if options.swc.filename.is_empty() {
FileName::Anon
} else {
FileName::Real(options.swc.filename.clone().into())
};
let fm = self.c.cm.new_source_file(filename, src.to_string());
let before_pass = custom_before_pass(&fm.name, &options);
self.c.process_js_with_custom_pass(
fm.clone(),
&handler,
&options.swc,
before_pass,
noop(),
)
}
})
})
}))
.map_err(|err| {
if let Some(s) = err.downcast_ref::<String>() {
anyhow!("failed to process {}", s)
} else {
anyhow!("failed to process")
}
});
match res {
Ok(res) => res.convert_err(),
Err(err) => Err(napi::Error::new(
Status::GenericFailure,
format!("{:?}", err),
)),
}
}
fn resolve(self, env: Env, result: Self::Output) -> napi::Result<Self::JsValue> {
complete_output(&env, result)
}
}
/// returns `compiler, (src / path), options, plugin, callback`
pub fn schedule_transform<F>(cx: CallContext, op: F) -> napi::Result<JsObject>
where
F: FnOnce(&Arc<Compiler>, String, bool, String) -> TransformTask,
{
let c = get_compiler(&cx);
let src = cx.get::<JsString>(0)?.into_utf8()?.as_str()?.to_owned();
let is_module = cx.get::<JsBoolean>(1)?;
let options = cx.get_buffer_as_string(2)?;
let task = op(&c, src, is_module.get_value()?, options);
cx.env.spawn(task).map(|t| t.promise_object())
}
pub fn exec_transform<F>(cx: CallContext, op: F) -> napi::Result<JsObject>
where
F: FnOnce(&Compiler, String, &TransformOptions) -> Result<Arc<SourceFile>, Error>,
{
let c = get_compiler(&cx);
let s = cx.get::<JsString>(0)?.into_utf8()?;
let is_module = cx.get::<JsBoolean>(1)?;
let mut options: TransformOptions = cx.get_deserialized(2)?;
options.swc.swcrc = false;
let output = try_with_handler(c.cm.clone(), true, |handler| {
c.run(|| {
if is_module.get_value()? {
let program: Program =
serde_json::from_str(s.as_str()?).context("failed to deserialize Program")?;
c.process_js(&handler, program, &options.swc)
} else {
let fm =
op(&c, s.as_str()?.to_string(), &options).context("failed to load file")?;
let before_pass = custom_before_pass(&fm.name, &options);
c.process_js_with_custom_pass(fm, &handler, &options.swc, before_pass, noop())
}
})
})
.convert_err()?;
complete_output(cx.env, output)
}
#[js_function(4)]
pub fn transform(cx: CallContext) -> napi::Result<JsObject> {
schedule_transform(cx, |c, src, _, options| {
let input = Input::Source { src };
TransformTask {
c: c.clone(),
input,
options,
}
})
}
#[js_function(4)]
pub fn transform_sync(cx: CallContext) -> napi::Result<JsObject> {
exec_transform(cx, |c, src, options| {
Ok(c.cm.new_source_file(
if options.swc.filename.is_empty() {
FileName::Anon
} else {
FileName::Real(options.swc.filename.clone().into())
},
src,
))
})
}
#[test]
fn test_deser() {
const JSON_STR: &str = r#"{"jsc":{"parser":{"syntax":"ecmascript","dynamicImport":true,"jsx":true},"transform":{"react":{"runtime":"automatic","pragma":"React.createElement","pragmaFrag":"React.Fragment","throwIfNamespace":true,"development":false,"useBuiltins":true}},"target":"es5"},"filename":"/Users/filename","sourceMaps":false,"sourceFileName":"/Users/sourceFilename" }"#;
let tr: TransformOptions = serde_json::from_str(&JSON_STR).unwrap();
println!("{:#?}", tr);
}
#[test]
fn test_deserialize_transform_regenerator() {
const JSON_STR: &str = r#"{"jsc":{"parser":{"syntax":"ecmascript","dynamicImport":true,"jsx":true},"transform":{ "regenerator": { "importPath": "foo" }, "react":{"runtime":"automatic","pragma":"React.createElement","pragmaFrag":"React.Fragment","throwIfNamespace":true,"development":false,"useBuiltins":true}},"target":"es5"},"filename":"/Users/sourceFilename","sourceMaps":false,"sourceFileName":"/Users/filename" }"#;
let tr: TransformOptions = serde_json::from_str(&JSON_STR).unwrap();
println!("{:#?}", tr);
}

View File

@ -1,56 +0,0 @@
use anyhow::{Context, Error};
use napi::{CallContext, JsBuffer, Status};
use serde::de::DeserializeOwned;
use std::any::type_name;
pub trait MapErr<T>: Into<Result<T, anyhow::Error>> {
fn convert_err(self) -> napi::Result<T> {
self.into()
.map_err(|err| napi::Error::new(Status::GenericFailure, format!("{:?}", err)))
}
}
impl<T> MapErr<T> for Result<T, anyhow::Error> {}
pub trait CtxtExt {
fn get_buffer_as_string(&self, index: usize) -> napi::Result<String>;
/// Currently this uses JsBuffer
fn get_deserialized<T>(&self, index: usize) -> napi::Result<T>
where
T: DeserializeOwned;
}
impl CtxtExt for CallContext<'_> {
fn get_buffer_as_string(&self, index: usize) -> napi::Result<String> {
let buffer = self.get::<JsBuffer>(index)?.into_value()?;
Ok(String::from_utf8_lossy(buffer.as_ref()).to_string())
}
fn get_deserialized<T>(&self, index: usize) -> napi::Result<T>
where
T: DeserializeOwned,
{
let buffer = self.get::<JsBuffer>(index)?.into_value()?;
let v = serde_json::from_slice(&buffer)
.with_context(|| {
format!(
"Failed to deserialize argument at `{}` as {}\nJSON: {}",
index,
type_name::<T>(),
String::from_utf8_lossy(&buffer)
)
})
.convert_err()?;
Ok(v)
}
}
pub(crate) fn deserialize_json<T>(s: &str) -> Result<T, Error>
where
T: DeserializeOwned,
{
serde_json::from_str(&s)
.with_context(|| format!("failed to deserialize as {}\nJSON: {}", type_name::<T>(), s))
}

View File

@ -1,35 +0,0 @@
use builder_swc::keep_platform::{keep_platform, KeepPlatformConfig};
use std::path::PathBuf;
use swc_ecma_transforms_testing::{test, test_fixture};
use swc_ecmascript::parser::{EsConfig, Syntax};
use testing::fixture;
fn unminify_syntax() -> Syntax {
Syntax::Es(EsConfig {
..Default::default()
})
}
#[fixture("tests/fixture/keep_platform/web/input.js")]
fn transform_web_flag_fixture(input: PathBuf) {
let output = input.parent().unwrap().join("output.js");
let config = KeepPlatformConfig::KeepPlatform(String::from("web"));
test_fixture(
unminify_syntax(),
&|_tr| keep_platform(config.clone()),
&input,
&output,
);
}
#[fixture("tests/fixture/keep_platform/kraken/input.js")]
fn transform_kraken_flag_fixture(input: PathBuf) {
let output = input.parent().unwrap().join("output.js");
let config = KeepPlatformConfig::KeepPlatform(String::from("kraken"));
test_fixture(
unminify_syntax(),
&|_tr| keep_platform(config.clone()),
&input,
&output,
);
}

View File

@ -1,17 +0,0 @@
import { isWeb, isWeex, isKraken } from 'universal-env';
if (isWeb) {
console.log('This is web');
} else if (isWeex) {
console.log('This is weex');
} else {
console.log('others1');
}
if (isKraken) {
console.log('This is kraken');
} else {
console.log('others2');
}

View File

@ -1,19 +0,0 @@
var isKraken = true;
var isWeex = false;
var isWeb = true;
if (isWeb) {
console.log('This is web');
} else if (isWeex) {
console.log('This is weex');
} else {
console.log('others1');
}
if (isKraken) {
console.log('This is kraken');
} else {
console.log('others2');
}

View File

@ -1,10 +0,0 @@
import { isWeb, isWeex } from 'universal-env';
if (isWeb) {
console.log('This is web');
} else if (isWeex) {
console.log('This is weex');
} else {
console.log('others');
}

View File

@ -1,9 +0,0 @@
var isWeex = false;
var isWeb = true;
if (isWeb) {
console.log("This is web");
} else if (isWeex) {
console.log("This is weex");
} else {
console.log("others");
}

View File

@ -1,115 +0,0 @@
use builder_swc::keep_platform::KeepPlatformConfig;
use builder_swc::{custom_before_pass, TransformOptions};
use serde::de::DeserializeOwned;
use std::path::{Path, PathBuf};
use swc::config::{OptimizerConfig, TransformConfig};
use swc::Compiler;
use swc_ecmascript::{
parser::{Syntax, TsConfig},
transforms::pass::noop,
};
use testing::{fixture, NormalizedOutput, Tester};
#[fixture("tests/minify/base_syntax/input.js")]
fn base_minify(input: PathBuf) {
test(&input, true, "".to_string());
}
#[fixture("tests/minify/remove_platform_code/web/input.js")]
fn save_web_code(input: PathBuf) {
test(&input, true, "web".to_string());
}
#[fixture("tests/minify/remove_platform_code/kraken/input.js")]
fn save_kraken_code(input: PathBuf) {
test(&input, true, "kraken".to_string());
}
#[fixture("tests/unminify/**/input.js")]
fn unminify(input: PathBuf) {
test(&input, false, "".to_string());
}
fn test(input: &Path, minify: bool, platform: String) {
let output = input.parent().unwrap().join("output.js");
let keep_platform: KeepPlatformConfig;
if platform == "" {
keep_platform = KeepPlatformConfig::Bool(false);
} else {
keep_platform = KeepPlatformConfig::KeepPlatform(platform);
}
Tester::new()
.print_errors(|cm, handler| {
let c = Compiler::new(cm.clone());
let fm = cm.load_file(input).expect("failed to load file");
let options = TransformOptions {
swc: swc::config::Options {
swcrc: true,
is_module: true,
output_path: Some(output.to_path_buf()),
config: swc::config::Config {
jsc: swc::config::JscConfig {
minify: if minify {
Some(assert_json(
"{ \"compress\": { \"dead_code\": true }, \"mangle\": true }",
))
} else {
None
},
syntax: Some(Syntax::Typescript(TsConfig {
tsx: true,
dynamic_import: true,
..Default::default()
})),
transform: Some(TransformConfig {
optimizer: Some(OptimizerConfig {
simplify: minify,
..Default::default()
}),
..Default::default()
}),
..Default::default()
},
minify: minify,
..Default::default()
},
..Default::default()
},
keep_platform: keep_platform,
..Default::default()
};
match c.process_js_with_custom_pass(
fm.clone(),
&handler,
&options.swc,
custom_before_pass(&fm.name, &options),
noop(),
) {
Ok(v) => {
NormalizedOutput::from(v.code)
.compare_to_file(output)
.unwrap();
}
Err(err) => panic!("Error: {:?}", err),
};
Ok(())
})
.map(|_| ())
.expect("failed");
}
/// Using this, we don't have to break code by adding field.s
fn assert_json<T>(json_str: &str) -> T
where
T: DeserializeOwned,
{
serde_json::from_str(json_str).expect("failed to deserialize")
}

View File

@ -1,21 +0,0 @@
import fs from 'fs'
import other from 'other'
const [a, b, ...rest] = fs.promises
const [foo, bar] = other
export async function getStaticProps() {
a
b
rest
bar
}
class Foo {}
export default function Home() {
return <div />
}

View File

@ -1 +0,0 @@
import c from"regenerator-runtime";import d from"fs";import e from"other";function _arrayWithHoles(c){if(Array.isArray(c))return c}function asyncGeneratorStep(l,d,e,m,n,h,i){try{var j=l[h](i),c=j.value}catch(l){e(l);return}j.done?d(c):Promise.resolve(c).then(m,n)}function _asyncToGenerator(h){return function(){var i=this,j=arguments;return new Promise(function(d,e){var l=h.apply(i,j);function m(c){asyncGeneratorStep(l,d,e,m,n,"next",c)}function n(l){asyncGeneratorStep(l,d,e,m,n,"throw",l)}m(void 0)})}}function _iterableToArray(c){if(Symbol.iterator in Object(c)||"[object Arguments]"===Object.prototype.toString.call(c))return Array.from(c)}function _iterableToArrayLimit(c,d){var f=[],g=!0,h=!1,i=void 0;try{for(var j,k=c[Symbol.iterator]();!(g=(j=k.next()).done)&&(f.push(j.value),!d||f.length!==d);g=!0);}catch(l){h=!0,i=l}finally{try{g||null==k.return||k.return()}finally{if(h)throw i}}return f}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}function _slicedToArray(c,d){return _arrayWithHoles(c)||_iterableToArrayLimit(c,d)||_nonIterableRest()}function _toArray(c){return _arrayWithHoles(c)||_iterableToArray(c)||_nonIterableRest()}var _promises=_toArray(d.promises),a=_promises[0],b=_promises[1],rest=_promises.slice(2),_other=_slicedToArray(e,2),foo=_other[0],bar=_other[1];function _getStaticProps(){return(_getStaticProps=_asyncToGenerator(c.mark(function d(){return c.wrap(function(c){for(;;)switch(c.prev=c.next){case 0:case"end":return c.stop()}},d)}))).apply(this,arguments)}export function getStaticProps(){return _getStaticProps.apply(this,arguments)}export default function c(){return React.createElement("div",null)}

View File

@ -1,16 +0,0 @@
import { isWeb, isWeex, isKraken } from 'universal-env';
if (isWeb) {
console.log('This is web');
} else if (isWeex) {
console.log('This is weex');
} else {
console.log('others1');
}
if (isKraken) {
console.log('This is kraken');
} else {
console.log('others2');
}

View File

@ -1 +0,0 @@
console.log("This is web"),console.log("This is kraken")

View File

@ -1,10 +0,0 @@
import { isWeb, isWeex } from 'universal-env';
if (isWeb) {
console.log('This is web');
} else if (isWeex) {
console.log('This is weex');
} else {
console.log('others');
}

View File

@ -1 +0,0 @@
console.log("This is web")

View File

@ -1,113 +0,0 @@
import { transformSync } from '../../node';
describe('swc transform code', () => {
it('should transform es6 code to es5', () => {
const originalCode = `const a = {
x: 1,
y: 2,
};
const b = {
z: 3,
...a,
};
`;
const { code } = transformSync(originalCode, {
sourceMaps: false,
jsc: {
parser: {
syntax: 'ecmascript',
},
target: 'es5'
}
});
expect(code).toEqual(`function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _objectSpread(target) {
for(var i = 1; i < arguments.length; i++){
var source = arguments[i] != null ? arguments[i] : {
};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === \"function\") {
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
}));
}
ownKeys.forEach(function(key) {
_defineProperty(target, key, source[key]);
});
}
return target;
}
var a = {
x: 1,
y: 2
};
var b = _objectSpread({
z: 3
}, a);
`);
});
it('should transform TypeScript to es2021', () => {
const originalCode = `interface IType {
name: string;
}
const a: IType = {
name: 'Hello',
};`;
const { code } = transformSync(originalCode, {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
target: 'es2021'
},
sourceMaps: false,
});
expect(code).toEqual(`const a = {
name: 'Hello'
};
`);
});
it('should transform JSX to createElement', () => {
const originalCode = `import React from 'react';
export default function Home() {
return <div>home page</div>;
}`;
const { code } = transformSync(originalCode, {
sourceMaps: false,
jsc: {
parser: {
syntax: 'ecmascript',
jsx: true,
},
target: 'es5'
},
});
expect(code).toEqual(`import React from 'react';
export default function Home() {
return(/*#__PURE__*/ React.createElement("div", null, "home page"));
};
`);
});
});

View File

@ -1,95 +0,0 @@
import { transformSync } from '../../node';
describe('swc remove multiple ends code', () => {
it('should keep original code with not config removeMultipleEndsCode', () => {
const originalCode = `import { isWeb } from 'universal-env';
if (isWeb) {
console.log('This is web');
} else {
console.log('This is others');
}
`;
const { code } = transformSync(originalCode, {
sourceMaps: false,
jsc: {
parser: {
syntax: 'ecmascript',
},
target: 'es5'
}
});
expect(code).toEqual(`import { isWeb } from 'universal-env';
if (isWeb) {
console.log('This is web');
} else {
console.log('This is others');
}
`);
});
it('should assign isWeb as true', () => {
const originalCode = `import { isWeb } from 'universal-env';
if (isWeb) {
console.log('This is web');
} else {
console.log('This is others');
}
`;
const { code } = transformSync(originalCode, {
sourceMaps: false,
jsc: {
parser: {
syntax: 'ecmascript',
},
target: 'es5'
},
keepPlatform: 'web'
});
expect(code).toEqual(`var isWeb = true;
if (isWeb) {
console.log('This is web');
} else {
console.log('This is others');
}
`);
});
it('should save web code', () => {
const originalCode = `import { isWeb, isWeex } from 'universal-env';
if (isWeb) {
console.log('This is web');
} else if (isWeex) {
console.log('This is weex');
} else {
console.log('others');
}
`;
const { code } = transformSync(originalCode, {
sourceMaps: false,
jsc: {
minify: {
compress: true,
mangle: true
},
parser: {
syntax: 'ecmascript',
},
target: 'es5',
transform: {
optimizer: {
simplify: true
}
}
},
keepPlatform: 'web',
minify: true
});
expect(code).toEqual('console.log("This is web")');
});
});

View File

@ -1,21 +0,0 @@
import fs from 'fs'
import other from 'other'
const [a, b, ...rest] = fs.promises
const [foo, bar] = other
export async function getStaticProps() {
a
b
rest
bar
}
class Foo {}
export default function Home() {
return <div />
}

View File

@ -1,103 +0,0 @@
import regeneratorRuntime from "regenerator-runtime";
import fs from 'fs';
import other from 'other';
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function() {
var self = this, args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
}
function _iterableToArrayLimit(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for(var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true){
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally{
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally{
if (_d) throw _e;
}
}
return _arr;
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
}
function _toArray(arr) {
return _arrayWithHoles(arr) || _iterableToArray(arr) || _nonIterableRest();
}
var _promises = _toArray(fs.promises), a = _promises[0], b = _promises[1], rest = _promises.slice(2);
var _other = _slicedToArray(other, 2), foo = _other[0], bar = _other[1];
function _getStaticProps() {
_getStaticProps = _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
return regeneratorRuntime.wrap(function _callee$(_ctx) {
while(1)switch(_ctx.prev = _ctx.next){
case 0:
a;
b;
rest;
bar;
case 4:
case "end":
return _ctx.stop();
}
}, _callee);
}));
return _getStaticProps.apply(this, arguments);
}
export function getStaticProps() {
return _getStaticProps.apply(this, arguments);
}
var Foo = function Foo() {
"use strict";
_classCallCheck(this, Foo);
};
export default function Home() {
return(/*#__PURE__*/ React.createElement("div", null));
};

View File

@ -1,13 +0,0 @@
{
"extends": "../../tsconfig.settings.json",
"compilerOptions": {
"baseUrl": "./node",
"rootDir": "./node",
"outDir": "lib",
"moduleResolution": "node"
},
"include": ["node/index.ts"],
"exclude": [
"src/**/*"
]
}

View File

@ -1,26 +0,0 @@
/* eslint-disable no-await-in-loop */
import { copyFileSync, readdirSync, existsSync } from 'fs-extra';
import { join } from 'path';
import { cwd } from 'process';
const NATIVE_PACKAGES_DIR = join(process.cwd(), 'packages/swc/npm');
function copy() {
const platforms = (readdirSync(NATIVE_PACKAGES_DIR)).filter(
(name) => name !== '.gitignore'
);
// eslint-disable-next-line no-restricted-syntax
for (const platform of platforms) {
const binaryName = `builder-swc.${platform}.node`;
const binaryPath = join(cwd(), 'packages/swc/native', binaryName);
if (existsSync(binaryPath)) {
console.log(`Copying ${binaryPath}`);
copyFileSync(
binaryPath,
join(NATIVE_PACKAGES_DIR, platform, binaryName)
);
}
}
}
copy();

View File

@ -4,7 +4,6 @@ import { getNpmInfo } from 'ice-npm-utils';
import * as semver from 'semver';
const TARGET_DIRECTORY = join(__dirname, '../packages');
const NATIVE_NPM_DIRECTORY = join(TARGET_DIRECTORY, 'swc/npm');
export interface IPackageInfo {
name: string;
@ -49,11 +48,7 @@ export async function getPackageInfos(distTag = ''): Promise<IPackageInfo[]> {
} else {
const packageFolders: string[] = readdirSync(TARGET_DIRECTORY)
.filter((filename) => filename[0] !== '.')
.map((packageFolder) => join(TARGET_DIRECTORY, packageFolder))
.concat(
readdirSync(NATIVE_NPM_DIRECTORY)
.map((packageFolder) => join(NATIVE_NPM_DIRECTORY, packageFolder))
);
.map((packageFolder) => join(TARGET_DIRECTORY, packageFolder));
console.log('[PUBLISH] Start check with following packages:');
await Promise.all(packageFolders.map(async (packageFolder) => {
const packageInfoPath = join(packageFolder, 'package.json');

View File

@ -3,7 +3,6 @@
"references": [
{ "path": "packages/webpack-plugin-query-loader"},
{ "path": "packages/runtime" },
{ "path": "packages/swc" },
{ "path": "packages/build-app-templates" },
{ "path": "packages/build-app-helpers" },
{ "path": "packages/plugin-fusion" },

1502
yarn.lock

File diff suppressed because it is too large Load Diff