Skip to content

Commit b0051ed

Browse files
committed
feat: implement a new version of serialize-javascript, much powerful than before
1 parent 0185198 commit b0051ed

18 files changed

+878
-505
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ es-legacy
44
umd
55
tslib
66
tses
7+
src/version.ts
8+
test/utils/version.ts
79
types-legacy/*/*.d.ts
810
node_modules
911
npm-debug.log

e2e/specs/version.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import playwright from '../../test/engines/playwright';
2+
import testVersion from '../../test/test-suites/version';
3+
4+
testVersion(playwright);

enum-plus-v3.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@
5252
- `enum.valuesEnum` method is removed, use `enum.toValueMap` instead.
5353
- The behavior of `enum.values` is changed. Use `enum.items` for the old behavior.
5454

55+
## Misc
56+
57+
- The warning message for trying to modify enum items has been removed. First, in order to avoid circular references within enum items (which would affect serialization), we removed the internal `proxy` and used `getter/setter` instead. However, this brought about another problem: when printing enum items in the browser console or node.js, the `key`, `value`, and `label` cannot display their values, but show `[Getter/Setter]` instead. This somewhat affects the debugging experience. Sorry @yyz945947732, you introduced this feature, which is very good, but after weighing the pros and cons, we still had to remove this feature.
58+
5559
## Bug Fixes
5660

5761
- Fix the issue where sourcemap files under the `lib` directory could not be parsed.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,17 @@
115115
],
116116
"scripts": {
117117
"build": "run-p build:*",
118-
"build:es": "run-s task:build-es task:update-es-enum task:add-es-extensions task:add-umd-banner",
119-
"build:es-legacy": "cross-env LEGACY=1 run-s task:build-es-legacy task:update-es-legacy-enum task:add-umd-banner task:add-es-legacy-extensions",
120-
"build:lib": "run-s ts2lib task:copy-lib task:copy-dts task:types-legacy",
121-
"build:tses": "run-s ts2es task:add-tses-extensions",
118+
"build:es": "run-s gv task:build-es task:update-es-enum task:add-es-extensions task:add-umd-banner",
119+
"build:es-legacy": "cross-env LEGACY=1 run-s gv task:build-es-legacy task:update-es-legacy-enum task:add-umd-banner task:add-es-legacy-extensions",
120+
"build:lib": "run-s gv ts2lib task:copy-lib task:copy-dts task:types-legacy",
121+
"build:tses": "run-s gv ts2es task:add-tses-extensions",
122+
"clean": "shx rm -rf lib es es-legacy umd tses tslib types-legacy/pre-v5/*.d.ts",
122123
"e2e": "run-s task:bundle-e2e task:run-e2e",
123124
"e2e:debug": "run-s task:bundle-e2e task:run-e2e-debug",
124125
"e2e:ui": "run-s task:bundle-e2e task:run-e2e-ui",
125-
"prepare": "husky",
126+
"gv": "node scripts/generate-version.js",
127+
"init-husky": "husky",
128+
"prepare": "run-s gv init-husky",
126129
"prepare-legacy-node": "node ./scripts/prepare-legacy.js",
127130
"prepublishOnly": "run-s build test-all",
128131
"task:add-es-extensions": "tsx scripts/add-esm-extensions.ts es",
@@ -147,8 +150,8 @@
147150
"task:update-tses-enum": "tsx scripts/update-enum-ts.ts tses",
148151
"test": "npm run task:jest",
149152
"test-all": "run-s task:jest test-node-cjs test-node-esm e2e",
150-
"test-node-cjs": "run-s ts2lib task:jest-cjs",
151-
"test-node-esm": "run-s build:tses task:jest-esm",
153+
"test-node-cjs": "run-s build:lib ts2lib task:jest-cjs",
154+
"test-node-esm": "run-s build:es build:tses task:jest-esm",
152155
"ts2es": "tsc -p tsconfig.es.json",
153156
"ts2lib": "tsc -p tsconfig.lib.json"
154157
},

scripts/add-esm-extensions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function processDir(dir: string) {
2121
} else if (file.endsWith('.js')) {
2222
let content = readFileSync(fullPath, 'utf8');
2323
content = content.replace(/from\s+(['"])([./][^'"]+)['"]/g, (match, quote, importPath) => {
24-
if (!importPath.endsWith('.js')) {
24+
if (!importPath.endsWith('.js') && !importPath.endsWith('.json')) {
2525
return `from ${quote}${importPath}.js${quote}`;
2626
}
2727
return match;

scripts/generate-version.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const path = require('path');
2+
const fs = require('fs');
3+
4+
const root = path.resolve(__dirname, '..');
5+
6+
generate(path.join(root, 'src/version.ts'));
7+
generate(path.join(root, 'test/utils/version.ts'));
8+
9+
/** @param {string} outFile */
10+
function generate(outFile) {
11+
try {
12+
const pkgPath = path.join(root, 'package.json');
13+
const pkgRaw = fs.readFileSync(pkgPath, 'utf8');
14+
const pkg = JSON.parse(pkgRaw);
15+
const version = pkg.version;
16+
if (!version || typeof version !== 'string') {
17+
throw new Error('Missing version field in package.json');
18+
}
19+
20+
const content = `export const version = '${version}';\n`;
21+
22+
if (fs.existsSync(outFile)) {
23+
const existing = fs.readFileSync(outFile, 'utf8');
24+
if (existing !== content) {
25+
fs.writeFileSync(outFile, content, 'utf8');
26+
}
27+
} else {
28+
fs.writeFileSync(outFile, content, 'utf8');
29+
}
30+
} catch (err) {
31+
console.error('Error generating version.ts:', err);
32+
process.exitCode = 1;
33+
}
34+
}

scripts/make-e2e-bundle.ts

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { nodeResolve } from '@rollup/plugin-node-resolve';
2-
import { cpSync, readFileSync, writeFileSync } from 'fs';
32
import { rollup } from 'rollup';
43

54
async function build() {
@@ -26,60 +25,40 @@ async function build() {
2625
});
2726

2827
// Bundle serialize-javascript
29-
const serializePath = './e2e/fixtures/scripts/serialize-javascript.js';
30-
changeFormat('./tslib/test/utils/serialize-javascript.js', serializePath, 'SerializeJavascript');
3128
const serializeJavascript = await rollup({
32-
input: serializePath,
29+
input: 'tses/test/utils/serialize-javascript.js',
3330
plugins: [nodeResolve()],
3431
});
3532
await serializeJavascript.write({
3633
file: 'e2e/fixtures/scripts/serialize-javascript-bundle.js',
3734
format: 'iife',
35+
name: 'SerializeJavascript',
3836
});
3937

4038
// Bundle week-config
41-
const weekConfigPath = './e2e/fixtures/scripts/week-config.js';
42-
changeFormat('./tslib/test/data/week-config.js', weekConfigPath, 'WeekConfig', (content) => {
43-
return content.replace('require("../src")', 'window.EnumPlus');
44-
});
4539
const weekConfig = await rollup({
46-
input: weekConfigPath,
40+
input: 'tses/test/data/week-config.js',
4741
plugins: [nodeResolve()],
4842
});
4943
await weekConfig.write({
5044
file: 'e2e/fixtures/scripts/week-config-bundle.js',
5145
format: 'iife',
46+
name: 'WeekConfig',
5247
});
5348

5449
// Bundle week-data
55-
const weekDataPath = './e2e/fixtures/scripts/week-data.js';
56-
changeFormat('./tslib/test/data/week-data.js', weekDataPath, 'WeekData');
5750
const weekDataConfig = await rollup({
58-
input: weekDataPath,
51+
input: 'tses/test/data/week-data.js',
5952
plugins: [nodeResolve()],
6053
});
6154
await weekDataConfig.write({
6255
file: 'e2e/fixtures/scripts/week-data-bundle.js',
6356
format: 'iife',
57+
name: 'WeekData',
6458
});
6559
}
6660

6761
build().catch((e) => {
6862
console.error(e);
6963
process.exit(1);
7064
});
71-
72-
function changeFormat(src: string, dest: string, name: string, replacer?: (content: string) => string) {
73-
cpSync(src, dest, {
74-
force: true,
75-
});
76-
let content = readFileSync(dest, 'utf-8');
77-
content = `var exports = {};\n` + content;
78-
if (name) {
79-
content += `\n window.${name} = exports;`;
80-
}
81-
if (replacer) {
82-
content = replacer(content);
83-
}
84-
writeFileSync(dest, content);
85-
}

src/enum-collection.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ export class EnumCollectionClass<
4242
implements
4343
Omit<IEnumItems<T, K, V>, typeof IS_ENUM_ITEMS | typeof ITEMS | typeof KEYS | typeof VALUES | 'labels' | 'meta'>
4444
{
45-
private __options__: EnumItemOptions | undefined;
45+
private readonly __options__: EnumItemOptions | undefined;
46+
// used for e2e serialization
4647
private readonly __items__!: EnumItemsArray<T, K, V>;
4748

4849
constructor(init: T = {} as T, options?: EnumItemOptions) {
@@ -87,7 +88,9 @@ export class EnumCollectionClass<
8788
Object.defineProperty(this, keys.includes('labels') ? LABELS : 'labels', {
8889
enumerable: true,
8990
configurable: false,
90-
get: () => items.labels,
91+
get: function (this: EnumCollectionClass<T, K, V>) {
92+
return this.__items__.labels;
93+
},
9194
});
9295

9396
Object.freeze(this);

src/enum-item.ts

Lines changed: 37 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export class EnumItemClass<
1616
V extends EnumValue = ValueTypeFromSingleInit<T, K>,
1717
> {
1818
private _options: EnumItemOptions | undefined;
19+
private _label: string | undefined;
20+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
21+
private _localize: (content: string | undefined) => any;
1922

2023
/**
2124
* Instantiate an enum item
@@ -31,64 +34,61 @@ export class EnumItemClass<
3134
this.value = value;
3235
this.label = label;
3336
this.raw = raw;
34-
const isNode = typeof process !== 'undefined' && process && process.versions && process.versions.node != null;
35-
// Do not use class field here, because don't want print this field in Node.js
36-
Object.defineProperty(this, '_options', {
37-
value: options,
37+
38+
Object.defineProperty(this, '_label', {
39+
value: label,
3840
writable: false,
3941
enumerable: false,
4042
configurable: false,
4143
});
4244
Object.defineProperties(this, {
4345
value: {
44-
// To print pretty in Node.js console
45-
...(isNode
46-
? {
47-
value,
48-
writable: false,
49-
}
50-
: /* istanbul ignore next */ {
51-
get: /* istanbul ignore next */ () => value,
52-
set: /* istanbul ignore next */ () => console.warn(this._readonlyPropWarning('value')),
53-
}),
46+
value,
47+
writable: false,
5448
enumerable: true,
5549
configurable: false,
5650
},
5751
label: {
58-
get: () => this._localize(label) ?? label,
59-
set: () => console.warn(this._readonlyPropWarning('label')),
52+
get: function (this: EnumItemClass<T, K, V>) {
53+
return this._localize(this._label) ?? this._label;
54+
},
6055
enumerable: true,
6156
configurable: false,
6257
},
6358
key: {
64-
// To print pretty in Node.js console
65-
...(isNode
66-
? {
67-
value: key,
68-
writable: false,
69-
}
70-
: /* istanbul ignore next */ {
71-
get: /* istanbul ignore next */ () => key,
72-
set: /* istanbul ignore next */ () => console.warn(this._readonlyPropWarning('key')),
73-
}),
59+
value: key,
60+
writable: false,
7461
enumerable: true,
7562
configurable: false,
7663
},
7764
raw: {
78-
// To print pretty in Node.js console
79-
...(isNode
80-
? {
81-
value: raw,
82-
writable: false,
83-
}
84-
: /* istanbul ignore next */ {
85-
get: /* istanbul ignore next */ () => raw,
86-
set: /* istanbul ignore next */ () => console.warn(this._readonlyPropWarning('raw')),
87-
}),
65+
value: raw,
66+
writable: false,
8867
enumerable: true,
8968
configurable: false,
9069
},
9170
});
71+
// Do not use class field here, because don't want print this field in Node.js
72+
Object.defineProperty(this, '_options', {
73+
value: options,
74+
writable: false,
75+
enumerable: false,
76+
configurable: false,
77+
});
78+
this._localize = undefined!;
79+
Object.defineProperty(this, '_localize', {
80+
value: function (this: EnumItemClass<T, K, V>, content: string | undefined) {
81+
const localize = this._options?.localize ?? localizer.localize;
82+
if (typeof localize === 'function') {
83+
return localize(content);
84+
}
85+
return content;
86+
},
87+
writable: false,
88+
enumerable: false,
89+
configurable: false,
90+
});
91+
9292
Object.freeze(this);
9393
}
9494

@@ -149,21 +149,9 @@ export class EnumItemClass<
149149
return this.valueOf();
150150
}
151151

152-
// should use function here to avoid closure. this is important for the e2e test cases.
153-
private _localize(content: string | undefined) {
154-
const localize = this._options?.localize ?? localizer.localize;
155-
if (typeof localize === 'function') {
156-
return localize(content);
157-
}
158-
return content;
159-
}
160-
private _readonlyPropWarning(name: string) {
161-
return `Cannot modify property "${name}" on EnumItem. EnumItem instances are readonly and should not be mutated.`;
162-
}
163-
164152
// The priority of the toString method is lower than the valueOf method
165153
toString() {
166-
return this._localize(this.label);
154+
return this.label;
167155
}
168156
toLocaleString() {
169157
return this.toString();

0 commit comments

Comments
 (0)