Skip to content

Commit 78a675a

Browse files
committed
feat: implement this project
1 parent 21c8e98 commit 78a675a

File tree

13 files changed

+1239
-0
lines changed

13 files changed

+1239
-0
lines changed

.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
dist

.eslintrc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"root": true,
3+
"parser": "@typescript-eslint/parser",
4+
"plugins": [
5+
"@typescript-eslint"
6+
],
7+
"extends": [
8+
"eslint:recommended",
9+
"plugin:@typescript-eslint/recommended"
10+
],
11+
"rules": {
12+
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }]
13+
}
14+
}

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules/
2+
dist/
3+
4+
*.log
5+
*.swp
6+
.DS_Store

.vscode/extensions.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"recommendations": [
3+
"dprint.dprint"
4+
]
5+
}

.vscode/settings.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"editor.formatOnSave": true,
3+
"[javascript]": {
4+
"editor.defaultFormatter": "dprint.dprint"
5+
},
6+
"[typescript]": {
7+
"editor.defaultFormatter": "dprint.dprint"
8+
},
9+
"[json]": {
10+
"editor.defaultFormatter": "dprint.dprint"
11+
},
12+
"dprint.path": "./node_modules/dprint/bin.js",
13+
}

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# ts-transformer-optimize-const-enum
2+
3+
A typescript transpiler that transform exported const enum into object literal! This is just like the one from [@babel/preset-typescript or @babel/plugin-transform-typescript with optimizeConstEnums: true](https://babeljs.io/docs/en/babel-preset-typescript#optimizeconstenums) but it works for typescript compiler.
4+
5+
This will transform exported const enum from
6+
7+
```ts
8+
export const enum MyEnum {
9+
A,
10+
B,
11+
C
12+
D = 10,
13+
D = C * 200
14+
}
15+
```
16+
17+
into object literal like this
18+
19+
```ts
20+
export const MyEnum {
21+
A: 0,
22+
B: 1,
23+
C: 2,
24+
D: 10,
25+
E: 400
26+
}
27+
```
28+
29+
while stripping const in declaration file, to make your code compatible with `--isolatedModules`
30+
31+
```ts
32+
declare enum MyEnum { ... }
33+
```
34+
35+
## Why?
36+
WIP
37+
38+
# Usage
39+
40+
WIP
41+
42+
# Caveats
43+
44+
Currently, only immediate export const enum works. For example:
45+
This will be fixed in later version.
46+
47+
```ts
48+
// The following works
49+
export const enum WorkingEnum {}
50+
51+
// The following doesn't work
52+
const enum FailingEnum {}
53+
export FailEnum;
54+
```
55+

dprint.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"incremental": true,
3+
"indentWidth": 2,
4+
"typescript": {
5+
"arrowFunction.useParentheses": "preferNone",
6+
"quoteStyle": "preferSingle"
7+
},
8+
"json": {
9+
},
10+
"markdown": {
11+
},
12+
"includes": ["**/*.{ts,tsx,js,jsx,cjs,mjs,json,md}"],
13+
"excludes": [
14+
"**/node_modules",
15+
"**/*-lock.json"
16+
],
17+
"plugins": [
18+
"https://plugins.dprint.dev/typescript-0.62.2.wasm",
19+
"https://plugins.dprint.dev/json-0.14.1.wasm",
20+
"https://plugins.dprint.dev/markdown-0.12.2.wasm"
21+
]
22+
}

package.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "ts-transformer-optimize-const-enum",
3+
"version": "0.0.1",
4+
"description": "A TypeScript transformer that optimize exported const enum into object literal",
5+
"main": "dist/cjs/index.js",
6+
"module": "dist/ejs/index.js",
7+
"types": "dist/cjs/index.d.ts",
8+
"repository": "git@github.com:Fonger/ts-transformer-optimize-const-enum.git",
9+
"author": "Fonger <5862369+Fonger@users.noreply.github.com>",
10+
"license": "MIT",
11+
"keywords": [
12+
"typescript",
13+
"typescript-transformer",
14+
"transformer",
15+
"optimize",
16+
"const-enum",
17+
"object-literal",
18+
"ttypescript"
19+
],
20+
"scripts": {
21+
"build": "rm -rf dist && yarn build:cjs && yarn build:ejs",
22+
"build:cjs": "tsc --outDir dist/cjs --declaration",
23+
"build:ejs": "tsc --outDir dist/ejs --module es6"
24+
},
25+
"devDependencies": {
26+
"@typescript-eslint/eslint-plugin": "^5.10.2",
27+
"@typescript-eslint/parser": "^5.10.2",
28+
"dprint": "^0.22.0",
29+
"eslint": "^8.8.0",
30+
"ttypescript": "^1.5.13",
31+
"typescript": "^4.5.5"
32+
},
33+
"peerDependencies": {
34+
"typescript": ">=3.2.2"
35+
}
36+
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as transform } from './transform';

src/transform.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import ts from 'typescript';
2+
import { evaluate, hasModifier } from './utils';
3+
4+
export default function(program: ts.Program, pluginOptions: unknown) {
5+
return (ctx: ts.TransformationContext) => {
6+
return (sourceFile: ts.SourceFile) => {
7+
const ambient = sourceFile.isDeclarationFile;
8+
9+
return ts.visitEachChild(sourceFile, visitor, ctx);
10+
11+
function visitor(node: ts.Node): ts.Node {
12+
if (!ts.isEnumDeclaration(node)) {
13+
return ts.visitEachChild(node, visitor, ctx);
14+
}
15+
if (
16+
!hasModifier(node, ts.SyntaxKind.ExportKeyword)
17+
|| !hasModifier(node, ts.SyntaxKind.ConstKeyword)
18+
) {
19+
return node;
20+
}
21+
22+
if (ambient) {
23+
return ts.visitEachChild(node, stripConstKeyword, ctx);
24+
}
25+
26+
return transformEnum(node);
27+
}
28+
};
29+
};
30+
31+
function stripConstKeyword(node: ts.Node) {
32+
return node.kind === ts.SyntaxKind.ConstKeyword ? undefined : node;
33+
}
34+
35+
function transformEnum(node: ts.EnumDeclaration) {
36+
const members = node.members;
37+
const known = new Map<string, number | string>();
38+
const ast = [];
39+
let lastValue: string | number = -1;
40+
41+
for (const member of members) {
42+
if (!ts.isIdentifier(member.name)) {
43+
throw new Error('The name of enum member must be an identifier');
44+
}
45+
const name = member.name.getText();
46+
let value: string | number;
47+
48+
if (member.initializer) {
49+
value = evaluate(member.initializer, known);
50+
let valueExpr: ts.Expression;
51+
if (typeof value === 'number') {
52+
valueExpr = ts.factory.createNumericLiteral(value);
53+
lastValue = value;
54+
} else if (typeof value === 'string') {
55+
valueExpr = ts.factory.createStringLiteral(value);
56+
lastValue = value;
57+
} else throw new Error('Unsupported member value');
58+
59+
ast.push(ts.factory.createPropertyAssignment(name, valueExpr));
60+
} else {
61+
if (typeof lastValue === 'string') {
62+
throw new Error('Invalid enum');
63+
}
64+
/**
65+
* last known value+1
66+
* enum MyEnum {
67+
* A = 5,
68+
* B, // implicit 6
69+
* C, // implicit 7,
70+
* D = 1000,
71+
* E, // implicit 1001
72+
* }
73+
*/
74+
value = ++lastValue;
75+
ast.push(
76+
ts.factory.createPropertyAssignment(
77+
name,
78+
ts.factory.createNumericLiteral(value),
79+
),
80+
);
81+
}
82+
known.set(name, value);
83+
}
84+
85+
return ts.factory.createVariableStatement(
86+
[
87+
ts.factory.createModifier(ts.SyntaxKind.ExportKeyword),
88+
ts.factory.createModifier(ts.SyntaxKind.ConstKeyword),
89+
],
90+
ts.factory.createVariableDeclarationList(
91+
[
92+
ts.factory.createVariableDeclaration(
93+
node.name,
94+
undefined,
95+
undefined,
96+
ts.factory.createAsExpression(
97+
ts.factory.createObjectLiteralExpression(ast, true),
98+
ts.factory.createTypeReferenceNode(
99+
ts.factory.createIdentifier('const'),
100+
undefined,
101+
),
102+
),
103+
),
104+
],
105+
ts.NodeFlags.Const,
106+
),
107+
);
108+
}
109+
}

0 commit comments

Comments
 (0)