Overview
There does not seem to be a way to run something along the lines of this, and have it pick up the .swcrc file:
node -r @swc-node/register src/test.ts
Even the suggested SWCRC environment variable doesn't work:
SWCRC=1 node -r @swc-node/register src/test.ts
Configuration
Create a simple project which uses decorators, which are not enabled by default in swc. (There's nothing special about decorators, just that they are something which requires enabling explicitly in the config.)
.swcrc
Note decorators are enabled here.
{
"$schema": "https://json.schemastore.org/swcrc",
"env": {
"targets": {
"node": "18.12.1"
}
},
"jsc": {
"externalHelpers": false,
"keepClassNames": true,
"loose": false,
"parser": {
"decorators": true,
"decoratorsBeforeExport": true,
"dynamicImport": false,
"exportDefaultFrom": false,
"exportNamespaceFrom": false,
"functionBind": false,
"importMeta": false,
"jsx": false,
"privateMethod": false,
"syntax": "typescript",
"topLevelAwait": false
},
"target": "es2016",
"transform": {
"decoratorMetadata": true,
"legacyDecorator": true
}
},
"minify": false
}
package.json
Note that this includes ts-node as a way to show the expected behavior.
{
"devDependencies": {
"@swc-node/register": "1.6.2",
"@swc/cli": "0.1.62",
"@swc/core": "1.3.36",
"@tsconfig/node-lts-strictest": "18.12.1",
"ts-node": "10.9.1",
"typescript": "4.9.5"
},
"engines": {
"node": ">=18.12.1 <19",
"npm": ">=8.19.2 <9"
},
"name": "swc-decorator-test",
"private": "true",
"scripts": {
"swc-cli": "swc ./src -d build",
"swc-node": "node -r @swc-node/register src/test.ts",
"swc-node:withConfig": "SWCRC=1 node -r @swc-node/register src/test.ts",
"ts-node": "node -r ts-node/register src/test.ts"
},
"version": "1.0.0"
}
tsconfig.json
{
"extends": "@tsconfig/node-lts-strictest/tsconfig.json",
"compilerOptions": {
"experimentalDecorators": true
}
}
src/cached.ts
The decorator implementation — a really simple memoizer. Again, the details here don't actually matter, but I wanted something working.
export const cached = <T>(
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> => {
const funcName = `@cached(${(target as Function).name}.${String(propertyKey)})`;
const getter = descriptor.get;
if (getter == null || getter.length > 0) {
throw new Error(`Expected a get function: ${JSON.stringify(descriptor)}`);
}
const values = new WeakMap();
const wrapped = ({
[funcName](): T {
if (values.has(this)) {
return values.get(this);
}
const result = getter.apply(this);
values.set(this, result);
return result;
}
})[funcName] as () => T;
return {
...descriptor,
get: wrapped,
}
};
src/color.ts
This is where the problems will happen, as it's where the decorator is used.
import {cached} from "./cached";
export class Color {
constructor(
public readonly name: string,
public readonly hex: string,
) {
if (!/^[0-9a-fA-F]{6,8}$/.test(hex)) {
throw new Error(`Invalid hex code, 6 hex digits expected: ${hex}`);
}
}
@cached
public get rgb(): string {
return this.hex.replace(/^(..)(..)(..)(?:..)?$/, (_h, hr, hg, hb) => {
const r = parseInt(hr, 16);
const g = parseInt(hg, 16);
const b = parseInt(hb, 16);
return `rgb(${r}, ${g}, ${b})`;
});
}
}
src/test.ts
The script which will actually be run.
import {Color} from "./color";
const rebeccaPurple = new Color("rebeccapurple", "663399");
console.log(rebeccaPurple.rgb);
Steps to reproduce
-
Run: npm run swc-cli. Note that it compiles just fine:
Successfully compiled: 3 files with swc (7.2ms)
-
Temporarily rename .swcrc to something else and retry that previous step. Note the expected failure, as swc doesn't like decorators but can no longer see the config:
> swc ./src -d build
× Unexpected token `@`. Expected identifier, string literal, numeric literal or [ for the computed key
╭─[src/color.ts:10:1]
10 │ }
11 │ }
12 │
13 │ @cached
· ─
14 │ public get rgb(): string {
15 │ return this.hex.replace(/^(..)(..)(..)(?:..)?$/, (_h, hr, hg, hb) => {
16 │ const r = parseInt(hr, 16);
╰────
Caused by:
Syntax Error
-
Rename the config file back to .swcrc and ensure the cli can compile successfully again.
-
Run npm run ts-node. Note that it runs just fine:
rgb(102, 51, 153)
-
Run npm run swc-node. Note that it fails with the above error. (This is poor developer experience, based on how swc-cli picks up the config correctly, but y'all do you.)
-
Run npm run swc-node:withConfig, which uses the documented SWCRC environment variable. Note that it still fails.
Expected behavior
Personally, I'd prefer if register did the intuitive thing and picked up the .swcrc file. Barring that, I'd be happy if the SWCRC environment variable worked as documented. That's a bit awful, as it means all my scripts which look like node -r @swc-node/register ... will now need SWCRC=1 prefixed to them ... but that's not the end of the world.
Or, if there is some other way to make register see the .swcrc, I'm all ears.
Overview
There does not seem to be a way to run something along the lines of this, and have it pick up the
.swcrcfile:Even the suggested
SWCRCenvironment variable doesn't work:Configuration
Create a simple project which uses decorators, which are not enabled by default in swc. (There's nothing special about decorators, just that they are something which requires enabling explicitly in the config.)
.swcrcNote decorators are enabled here.
{ "$schema": "https://json.schemastore.org/swcrc", "env": { "targets": { "node": "18.12.1" } }, "jsc": { "externalHelpers": false, "keepClassNames": true, "loose": false, "parser": { "decorators": true, "decoratorsBeforeExport": true, "dynamicImport": false, "exportDefaultFrom": false, "exportNamespaceFrom": false, "functionBind": false, "importMeta": false, "jsx": false, "privateMethod": false, "syntax": "typescript", "topLevelAwait": false }, "target": "es2016", "transform": { "decoratorMetadata": true, "legacyDecorator": true } }, "minify": false }package.jsonNote that this includes ts-node as a way to show the expected behavior.
{ "devDependencies": { "@swc-node/register": "1.6.2", "@swc/cli": "0.1.62", "@swc/core": "1.3.36", "@tsconfig/node-lts-strictest": "18.12.1", "ts-node": "10.9.1", "typescript": "4.9.5" }, "engines": { "node": ">=18.12.1 <19", "npm": ">=8.19.2 <9" }, "name": "swc-decorator-test", "private": "true", "scripts": { "swc-cli": "swc ./src -d build", "swc-node": "node -r @swc-node/register src/test.ts", "swc-node:withConfig": "SWCRC=1 node -r @swc-node/register src/test.ts", "ts-node": "node -r ts-node/register src/test.ts" }, "version": "1.0.0" }tsconfig.json{ "extends": "@tsconfig/node-lts-strictest/tsconfig.json", "compilerOptions": { "experimentalDecorators": true } }src/cached.tsThe decorator implementation — a really simple memoizer. Again, the details here don't actually matter, but I wanted something working.
src/color.tsThis is where the problems will happen, as it's where the decorator is used.
src/test.tsThe script which will actually be run.
Steps to reproduce
Run:
npm run swc-cli. Note that it compiles just fine:Temporarily rename
.swcrcto something else and retry that previous step. Note the expected failure, as swc doesn't like decorators but can no longer see the config:Rename the config file back to
.swcrcand ensure the cli can compile successfully again.Run
npm run ts-node. Note that it runs just fine:Run
npm run swc-node. Note that it fails with the above error. (This is poor developer experience, based on how swc-cli picks up the config correctly, but y'all do you.)Run
npm run swc-node:withConfig, which uses the documentedSWCRCenvironment variable. Note that it still fails.Expected behavior
Personally, I'd prefer if register did the intuitive thing and picked up the
.swcrcfile. Barring that, I'd be happy if theSWCRCenvironment variable worked as documented. That's a bit awful, as it means all my scripts which look likenode -r @swc-node/register ...will now needSWCRC=1prefixed to them ... but that's not the end of the world.Or, if there is some other way to make register see the
.swcrc, I'm all ears.