Skip to content

Commit

Permalink
feat: add support for multiple swc configs. Closes #177. Don't enforc…
Browse files Browse the repository at this point in the history
…e importHelpers: true to avoid a potential tslib error when babel or swc is used for all types of syntax
  • Loading branch information
wessberg committed Jun 7, 2022
1 parent 8b3e7e8 commit 5ba4c9c
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 10 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
"prettier": "@wessberg/prettier-config",
"ava": {
"files": [
"test/**.test.ts"
"test/**.ts"
],
"verbose": true,
"timeout": "400s",
Expand Down
3 changes: 2 additions & 1 deletion src/plugin/typescript-plugin-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {TS} from "../type/ts.js";
import {DeclarationStats} from "../type/declaration-stats.js";
import {BabelConfig} from "../type/babel.js";
import {SwcConfig} from "../type/swc.js";
import { MaybeArray } from "helpertypes";

export type Transpiler = "typescript" | "babel" | "swc";

Expand Down Expand Up @@ -83,7 +84,7 @@ export interface TypescriptPluginOptions {
transpiler: Transpiler|TranspilerOptions;
tsconfig?: string | Partial<TS.CompilerOptions> | Partial<InputCompilerOptions> | TS.ParsedCommandLine | TsConfigResolver | TsConfigResolverWithFileName;
babelConfig?: string | Partial<BabelConfig>;
swcConfig?: string | Partial<SwcConfig>;
swcConfig?: string | MaybeArray<Partial<SwcConfig>>;
browserslist?: false | string[] | string | BrowserslistConfig;
cwd: string;
transformers?: (TS.CustomTransformers | CustomTransformersFunction)[] | TS.CustomTransformers | CustomTransformersFunction;
Expand Down
21 changes: 16 additions & 5 deletions src/transpiler/swc.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {Options} from "@swc/core";
import path from "crosspath";
import {MaybeArray} from "helpertypes";
import {FORCED_SWC_JSC_OPTIONS, FORCED_SWC_MODULE_OPTIONS} from "../constant/constant.js";
import {SwcConfigHook, TranspilationPhase, TypescriptPluginOptions} from "../plugin/typescript-plugin-options.js";
import {SwcConfig} from "../type/swc.js";
import {TS} from "../type/ts.js";
import {ensureArray} from "../util/ensure-array/ensure-array.js";
import {getEcmaVersionForScriptTarget} from "../util/get-script-target-from-browserslist/get-script-target-from-browserslist.js";
import { removeSearchPathFromFilename } from "../util/path/path-util.js";
import {removeSearchPathFromFilename} from "../util/path/path-util.js";
import {getTranspilerOptions} from "../util/plugin-options/get-plugin-options.js";

export interface GetSwcConfigOptions {
Expand All @@ -22,7 +24,7 @@ export interface GetSwcConfigOptions {

export type SwcConfigFactory = (filename: string, initial?: boolean) => Options | undefined;

function readConfig(config: TypescriptPluginOptions["swcConfig"], cwd: string, fileSystem: TS.System): SwcConfig {
function readConfig(config: TypescriptPluginOptions["swcConfig"], cwd: string, fileSystem: TS.System): MaybeArray<SwcConfig> {
if (config == null) {
const absoluteConfig = path.normalize(path.join(cwd, `.swcrc`));
if (!fileSystem.fileExists(absoluteConfig)) {
Expand All @@ -44,9 +46,19 @@ function readConfig(config: TypescriptPluginOptions["swcConfig"], cwd: string, f
* Gets a Swc Config based on the given options
*/
export function getSwcConfigFactory({fileSystem, swcConfig, cwd, browserslist, ecmaVersion, phase, typescript, hook, pluginOptions}: GetSwcConfigOptions): SwcConfigFactory {
const inputConfig = readConfig(swcConfig, cwd, fileSystem);
const inputConfigs = ensureArray(readConfig(swcConfig, cwd, fileSystem));

// Sanitize the 'test' properties of each individual input config
for (const inputConfig of inputConfigs) {
if (inputConfig.test != null) {
inputConfig.test = ensureArray(inputConfig.test).map(item => (item instanceof RegExp ? item : new RegExp(inputConfig.test)));
}
}

return (filename, initial = false) => {
// Select the best input config based on which one matches the test Regex(es) in the order they are declared in
const inputConfig = inputConfigs.find(config => config.test == null || config.test.some((regex: RegExp) => regex.test(filename))) ?? inputConfigs[0];

// Never allow minifying outside of the 'chunk' phase
const minify = phase === "file" ? false : Boolean(inputConfig.minify);

Expand All @@ -56,7 +68,6 @@ export function getSwcConfigFactory({fileSystem, swcConfig, cwd, browserslist, e
}

const syntax = inputConfig.jsc?.parser?.syntax ?? (initial ? "typescript" : "ecmascript");
getTranspilerOptions(pluginOptions.transpiler).otherSyntax !== "swc";

// If something else than swc handles syntax lowering, ensure that the Ecma version is set to ESNext to avoid anything else but TypeScript transformations
if (getTranspilerOptions(pluginOptions.transpiler).otherSyntax !== "swc") {
Expand All @@ -80,7 +91,7 @@ export function getSwcConfigFactory({fileSystem, swcConfig, cwd, browserslist, e
...(syntax === "typescript" ? {} : {jsx: false}),
...inputConfig.jsc?.parser
},
...(browserslist == null && ecmaVersion != null
...(browserslist == null && ecmaVersion != null && (inputConfig.env == null || Object.keys(inputConfig.env).length < 1)
? {
target: getEcmaVersionForScriptTarget(ecmaVersion, typescript)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {GetForcedCompilerOptionsOptions} from "./get-forced-compiler-options-opt
import {getScriptTargetFromBrowserslist} from "../get-script-target-from-browserslist/get-script-target-from-browserslist.js";
import {getOutDir} from "../get-out-dir/get-out-dir.js";
import {TS} from "../../type/ts.js";
import { getTranspilerOptions } from "../plugin-options/get-plugin-options.js";
import { getTranspilerOptions, isUsingTranspiler } from "../plugin-options/get-plugin-options.js";

/**
* Gets the ModuleKind to force
Expand Down Expand Up @@ -32,13 +32,29 @@ function getForcedScriptTargetOption({pluginOptions, browserslist}: GetForcedCom
return {};
}

/**
* Decide whether or not to force import helpers
*/
function getForcedImportHelpersOption({pluginOptions}: GetForcedCompilerOptionsOptions): {importHelpers?: boolean} {
// If TypeScript is being used, which uses tslib, helpers should *always* be imported.
// We don't want them to be duplicated multiple times within generated chunks.
// When other transpilers are being used in some shape of form, they'll have similar enforced options
if (isUsingTranspiler("typescript", getTranspilerOptions(pluginOptions.transpiler))) {
return {importHelpers: true};
}

// Otherwise, don't force the 'target' option
return {};
}

/**
* Retrieves the CompilerOptions that will be forced
*/
export function getForcedCompilerOptions(options: GetForcedCompilerOptionsOptions): Partial<TS.CompilerOptions> {
return {
...getForcedModuleKindOption(options),
...getForcedScriptTargetOption(options),
...getForcedImportHelpersOption(options),
outDir: getOutDir(options.pluginOptions.cwd),
// Rollup, not Typescript, is the decider of where to put files
outFile: undefined,
Expand All @@ -48,8 +64,6 @@ export function getForcedCompilerOptions(options: GetForcedCompilerOptionsOption
inlineSourceMap: false,
// Since we never use inline source maps, inline sources aren't supported
inlineSources: false,
// Helpers should *always* be imported. We don't want them to be duplicated multiple times within generated chunks
importHelpers: true,
// Node resolution is required when 'importHelpers' are true
moduleResolution: options.pluginOptions.typescript.ModuleResolutionKind.NodeJs,
// Typescript should always be able to emit - otherwise we cannot transform source files
Expand Down
75 changes: 75 additions & 0 deletions test/swc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,3 +458,78 @@ test.serial("Supports swc minification. #1", withTypeScript, async (t, {typescri
`)
);
});

test.serial("Supports multiple swc configurations. #1", withTypeScript, async (t, {typescript}) => {
const bundle = await generateRollupBundle(
[
{
entry: true,
fileName: "foo.ts",
text: `\
export const foo: number = 2;
`
},
{
entry: true,
fileName: "bar.js",
text: `\
export const bar = 2;
`
}
],
{
typescript,
transpiler: "swc",
swcConfig: [
{
test: ".ts$",
jsc: {
parser: {
syntax: "typescript"
}
},
env: {
targets: {
chrome: "100"
}
}
},
{
test: ".js$",
jsc: {
parser: {
syntax: "ecmascript"
}
},
env: {
targets: {
ie: "11"
}
}
}
]
}
);
const {
js: [fileA, fileB]
} = bundle;

t.deepEqual(
formatCode(fileA.code),
formatCode(`\
const foo = 2;
export { foo };
`)
);

t.deepEqual(
formatCode(fileB.code),
formatCode(`\
var bar = 2;
export { bar };
`)
);
});

0 comments on commit 5ba4c9c

Please sign in to comment.