wrec 0.40.2 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -15
- package/dist/{wrec-DHGadgxK.js → wrec-RkviIlbF.js} +17 -8
- package/dist/wrec-ssr.d.ts +1 -0
- package/dist/wrec-ssr.es.js +3 -3
- package/dist/wrec.d.ts +1 -0
- package/dist/wrec.es.js +1 -1
- package/package.json +39 -36
- package/scripts/ast-utils.js +20 -28
- package/scripts/declare.js +42 -62
- package/scripts/lint.js +609 -927
- package/scripts/scaffold.js +11 -13
- package/scripts/used-by.js +76 -119
package/README.md
CHANGED
|
@@ -72,11 +72,11 @@ Here are the steps:
|
|
|
72
72
|
"Prettier" to add syntax highlighting and format the CSS and HTML strings.
|
|
73
73
|
|
|
74
74
|
```js
|
|
75
|
-
import {css, html, Wrec} from
|
|
75
|
+
import { css, html, Wrec } from "wrec";
|
|
76
76
|
|
|
77
77
|
class MyCounter extends Wrec {
|
|
78
78
|
static properties = {
|
|
79
|
-
count: {type: Number}
|
|
79
|
+
count: { type: Number },
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
static css = css`
|
|
@@ -95,13 +95,7 @@ Here are the steps:
|
|
|
95
95
|
|
|
96
96
|
static html = html`
|
|
97
97
|
<div>
|
|
98
|
-
<button
|
|
99
|
-
onClick="this.count--"
|
|
100
|
-
type="button"
|
|
101
|
-
disabled="this.count === 0"
|
|
102
|
-
>
|
|
103
|
-
-
|
|
104
|
-
</button>
|
|
98
|
+
<button onClick="this.count--" type="button" disabled="this.count === 0">-</button>
|
|
105
99
|
<span>this.count</span>
|
|
106
100
|
<button onClick="this.count++" type="button">+</button>
|
|
107
101
|
<span>(this.count < 10 ? "single" : "multi") + "-digit"</span>
|
|
@@ -109,7 +103,7 @@ Here are the steps:
|
|
|
109
103
|
`;
|
|
110
104
|
}
|
|
111
105
|
|
|
112
|
-
MyCounter.define(
|
|
106
|
+
MyCounter.define("my-counter");
|
|
113
107
|
```
|
|
114
108
|
|
|
115
109
|
Property definitions can also constrain string values to an allowed set:
|
|
@@ -119,9 +113,9 @@ class TrafficLight extends Wrec {
|
|
|
119
113
|
static properties = {
|
|
120
114
|
color: {
|
|
121
115
|
type: String,
|
|
122
|
-
values: [
|
|
123
|
-
value:
|
|
124
|
-
}
|
|
116
|
+
values: ["red", "yellow", "green"],
|
|
117
|
+
value: "red",
|
|
118
|
+
},
|
|
125
119
|
};
|
|
126
120
|
}
|
|
127
121
|
```
|
|
@@ -245,10 +239,10 @@ to share state across multiple wrec components.
|
|
|
245
239
|
in the DevTools console as follows:
|
|
246
240
|
|
|
247
241
|
```js
|
|
248
|
-
state = WrecState.get(
|
|
242
|
+
state = WrecState.get("demo");
|
|
249
243
|
state; // to examine the entire object
|
|
250
244
|
state.name; // to see current value of "name" property
|
|
251
|
-
state.name =
|
|
245
|
+
state.name = "Earth"; // to modify "name" property
|
|
252
246
|
```
|
|
253
247
|
|
|
254
248
|
`traffic-light.html` demonstrates a property with an enumerated value.
|
|
@@ -514,7 +514,7 @@ var $ = class e extends O {
|
|
|
514
514
|
this.#R(t, n);
|
|
515
515
|
}
|
|
516
516
|
async connectedCallback() {
|
|
517
|
-
this.#X(), this.#h(), await this.#p(), this.hasAttribute("disabled") && this.#v(), this.#
|
|
517
|
+
this.#X(), this.#h(), await this.#p(), this.hasAttribute("disabled") && this.#v(), this.#ne(this.shadowRoot), this.#M(this.shadowRoot), this.#Y(), this.#m(), this.ready();
|
|
518
518
|
}
|
|
519
519
|
#m() {
|
|
520
520
|
let { properties: e } = this.#n;
|
|
@@ -541,8 +541,8 @@ var $ = class e extends O {
|
|
|
541
541
|
set(e) {
|
|
542
542
|
n.computed && !this.#t.has(t) && this.#V(null, t, "is a computed property and cannot be set directly"), s === Number && typeof e == "string" && (e = Y(e));
|
|
543
543
|
let r = this.#T(u);
|
|
544
|
-
if (e === r) return;
|
|
545
|
-
|
|
544
|
+
if (e === r || (this.#$(t, s, e), n.validate && !this.#ee(t, n, e))) return;
|
|
545
|
+
e = this.#j(t, s, e), this.#R(u, e);
|
|
546
546
|
let a = this.#l.get(t);
|
|
547
547
|
a && l(a.state, a.stateProp, e), this.#G(t, s, e, i), this.#e || (this.#K(t), this.#P(t)), this.#J(t, e);
|
|
548
548
|
let o = this.#i[t];
|
|
@@ -913,7 +913,7 @@ var $ = class e extends O {
|
|
|
913
913
|
else if (n && r === "string" && i.trim().startsWith("<")) {
|
|
914
914
|
let t = T(i);
|
|
915
915
|
if (e.innerHTML === t) return;
|
|
916
|
-
e.innerHTML = t, this.#
|
|
916
|
+
e.innerHTML = t, this.#ne(e), this.#M(e);
|
|
917
917
|
} else n && e.textContent !== i && (e.textContent = i);
|
|
918
918
|
}
|
|
919
919
|
#J(e, t) {
|
|
@@ -993,12 +993,12 @@ var $ = class e extends O {
|
|
|
993
993
|
let t = new Set(Object.keys(this.#n.properties));
|
|
994
994
|
for (let n of this.getAttributeNames()) if (!D.has(n) && !n.startsWith("on") && n !== "ref") {
|
|
995
995
|
if (n === "form-assoc") {
|
|
996
|
-
this.#
|
|
996
|
+
this.#te();
|
|
997
997
|
continue;
|
|
998
998
|
}
|
|
999
999
|
if (!t.has(e.getPropName(n))) {
|
|
1000
1000
|
if (n === "name") {
|
|
1001
|
-
this.#
|
|
1001
|
+
this.#te();
|
|
1002
1002
|
continue;
|
|
1003
1003
|
}
|
|
1004
1004
|
this.#V(null, n, "is not a supported attribute");
|
|
@@ -1032,12 +1032,21 @@ var $ = class e extends O {
|
|
|
1032
1032
|
}
|
|
1033
1033
|
i !== t.name.toLowerCase() && this.#V(null, e, `was set to a ${i}, but must be a ${t.name}`);
|
|
1034
1034
|
}
|
|
1035
|
-
#ee() {
|
|
1035
|
+
#ee(e, t, n) {
|
|
1036
|
+
let r = t.validate?.(n), i = typeof r != "string";
|
|
1037
|
+
return i || this.dispatch("validation", {
|
|
1038
|
+
object: this,
|
|
1039
|
+
property: e,
|
|
1040
|
+
value: n,
|
|
1041
|
+
message: r
|
|
1042
|
+
}), i;
|
|
1043
|
+
}
|
|
1044
|
+
#te() {
|
|
1036
1045
|
if (this.#n.formAssociated || this.closest("form") === null) return;
|
|
1037
1046
|
let e = this.#n.name;
|
|
1038
1047
|
this.#V(this, void 0, `inside form, class ${e} requires "static formAssociated = true;"`);
|
|
1039
1048
|
}
|
|
1040
|
-
#
|
|
1049
|
+
#ne(e) {
|
|
1041
1050
|
let t = Array.from(e.querySelectorAll("*"));
|
|
1042
1051
|
for (let e of t) {
|
|
1043
1052
|
let t = [];
|
package/dist/wrec-ssr.d.ts
CHANGED
package/dist/wrec-ssr.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c, u as l } from "./wrec-
|
|
1
|
+
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c, u as l } from "./wrec-RkviIlbF.js";
|
|
2
2
|
//#region \0rolldown/runtime.js
|
|
3
3
|
var u = Object.defineProperty, d = Object.getOwnPropertyDescriptor, f = Object.getOwnPropertyNames, p = Object.prototype.hasOwnProperty, m = (e, t) => () => (e && (t = e(e = 0)), t), h = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t.exports), g = (e, t) => {
|
|
4
4
|
let n = {};
|
|
@@ -7129,7 +7129,7 @@ var u = Object.defineProperty, d = Object.getOwnPropertyDescriptor, f = Object.g
|
|
|
7129
7129
|
})(L ||= {});
|
|
7130
7130
|
}));
|
|
7131
7131
|
//#endregion
|
|
7132
|
-
//#region node_modules/css-what/lib/es/parse.js
|
|
7132
|
+
//#region node_modules/.pnpm/css-what@6.2.2/node_modules/css-what/lib/es/parse.js
|
|
7133
7133
|
function ue(e) {
|
|
7134
7134
|
switch (e.type) {
|
|
7135
7135
|
case I.Adjacent:
|
|
@@ -7352,7 +7352,7 @@ var U, pe, me, he, ge, _e = m((() => {
|
|
|
7352
7352
|
]), ge = new Set(["contains", "icontains"]);
|
|
7353
7353
|
}));
|
|
7354
7354
|
//#endregion
|
|
7355
|
-
//#region node_modules/css-what/lib/es/stringify.js
|
|
7355
|
+
//#region node_modules/.pnpm/css-what@6.2.2/node_modules/css-what/lib/es/stringify.js
|
|
7356
7356
|
function ve(e) {
|
|
7357
7357
|
return e.map((e) => e.map(ye).join("")).join(", ");
|
|
7358
7358
|
}
|
package/dist/wrec.d.ts
CHANGED
package/dist/wrec.es.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as e, n as t, r as n, t as r, u as i } from "./wrec-
|
|
1
|
+
import { i as e, n as t, r as n, t as r, u as i } from "./wrec-RkviIlbF.js";
|
|
2
2
|
export { r as Wrec, i as WrecState, t as createElement, n as css, e as html };
|
package/package.json
CHANGED
|
@@ -1,21 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wrec",
|
|
3
|
+
"version": "0.42.0",
|
|
3
4
|
"description": "a library that greatly simplifies building web components",
|
|
4
|
-
"
|
|
5
|
-
|
|
5
|
+
"keywords": [
|
|
6
|
+
"component",
|
|
7
|
+
"web"
|
|
8
|
+
],
|
|
6
9
|
"license": "MIT",
|
|
10
|
+
"author": "R. Mark Volkmann",
|
|
7
11
|
"repository": {
|
|
8
12
|
"type": "git",
|
|
9
13
|
"url": "git+https://github.com/mvolkmann/wrec.git"
|
|
10
14
|
},
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
15
|
+
"bin": {
|
|
16
|
+
"wrec-declare": "./scripts/declare.js",
|
|
17
|
+
"wrec-lint": "./scripts/lint.js",
|
|
18
|
+
"wrec-scaffold": "./scripts/scaffold.js",
|
|
19
|
+
"wrec-usedby": "./scripts/used-by.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"scripts/ast-utils.js",
|
|
24
|
+
"scripts/declare.js",
|
|
25
|
+
"scripts/lint.js",
|
|
26
|
+
"scripts/scaffold.js",
|
|
27
|
+
"scripts/template.ts",
|
|
28
|
+
"scripts/used-by.js",
|
|
29
|
+
"README.md"
|
|
14
30
|
],
|
|
15
31
|
"type": "module",
|
|
16
32
|
"module": "./dist/wrec.es.js",
|
|
17
33
|
"types": "./dist/wrec.d.ts",
|
|
18
|
-
"moduleResolution": "bundler",
|
|
19
34
|
"exports": {
|
|
20
35
|
".": {
|
|
21
36
|
"types": "./dist/wrec.d.ts",
|
|
@@ -28,44 +43,32 @@
|
|
|
28
43
|
"import": "./dist/wrec-ssr.es.js"
|
|
29
44
|
}
|
|
30
45
|
},
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"wrec-usedby": "./scripts/used-by.js"
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"node-html-parser": "^7.1.0",
|
|
48
|
+
"typescript": "^6.0.2",
|
|
49
|
+
"xss": "^1.0.15"
|
|
36
50
|
},
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@playwright/test": "^1.59.1",
|
|
53
|
+
"@types/node": "^25.5.2",
|
|
54
|
+
"get-port": "^7.2.0",
|
|
55
|
+
"jsdom": "^29.0.2",
|
|
56
|
+
"oxfmt": "^0.51.0",
|
|
57
|
+
"oxlint": "^1.58.0",
|
|
58
|
+
"vite": "^8.0.3",
|
|
59
|
+
"vite-plugin-dts": "^4.5.4",
|
|
60
|
+
"vitest": "^4.1.7"
|
|
61
|
+
},
|
|
62
|
+
"moduleResolution": "bundler",
|
|
47
63
|
"scripts": {
|
|
48
64
|
"dev": "vite",
|
|
49
65
|
"build": "vite build",
|
|
50
66
|
"clean": "rm -rf dist",
|
|
67
|
+
"format": "oxfmt",
|
|
51
68
|
"lint": "oxlint",
|
|
52
69
|
"preview": "vite preview",
|
|
53
70
|
"test": "node port-check.js && playwright test",
|
|
54
71
|
"testui": "playwright test --ui",
|
|
55
72
|
"unittest": "vitest run"
|
|
56
|
-
},
|
|
57
|
-
"devDependencies": {
|
|
58
|
-
"@playwright/test": "^1.59.1",
|
|
59
|
-
"@types/node": "^25.5.2",
|
|
60
|
-
"get-port": "^7.2.0",
|
|
61
|
-
"jsdom": "^29.0.2",
|
|
62
|
-
"oxlint": "^1.58.0",
|
|
63
|
-
"vite": "^8.0.3",
|
|
64
|
-
"vite-plugin-dts": "^4.5.4"
|
|
65
|
-
},
|
|
66
|
-
"dependencies": {
|
|
67
|
-
"node-html-parser": "^7.1.0",
|
|
68
|
-
"typescript": "^6.0.2",
|
|
69
|
-
"xss": "^1.0.15"
|
|
70
73
|
}
|
|
71
|
-
}
|
|
74
|
+
}
|
package/scripts/ast-utils.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import ts from
|
|
1
|
+
import ts from "typescript";
|
|
2
2
|
|
|
3
3
|
// Finds all classes in a source file that extend Wrec.
|
|
4
4
|
export function collectWrecClasses(sourceFile) {
|
|
5
|
-
const {names: wrecNames} = getWrecImportInfo(sourceFile);
|
|
5
|
+
const { names: wrecNames } = getWrecImportInfo(sourceFile);
|
|
6
6
|
const classes = [];
|
|
7
7
|
|
|
8
8
|
function visit(node) {
|
|
9
|
-
if (
|
|
10
|
-
ts.isClassDeclaration(node) &&
|
|
11
|
-
node.name &&
|
|
12
|
-
extendsWrec(node, wrecNames)
|
|
13
|
-
) {
|
|
9
|
+
if (ts.isClassDeclaration(node) && node.name && extendsWrec(node, wrecNames)) {
|
|
14
10
|
classes.push(node);
|
|
15
11
|
}
|
|
16
12
|
ts.forEachChild(node, visit);
|
|
@@ -24,29 +20,27 @@ export function collectWrecClasses(sourceFile) {
|
|
|
24
20
|
export function extendsWrec(classNode, wrecNames) {
|
|
25
21
|
return Boolean(
|
|
26
22
|
classNode.heritageClauses?.some(
|
|
27
|
-
clause =>
|
|
23
|
+
(clause) =>
|
|
28
24
|
clause.token === ts.SyntaxKind.ExtendsKeyword &&
|
|
29
25
|
clause.types.some(
|
|
30
|
-
type =>
|
|
26
|
+
(type) =>
|
|
31
27
|
ts.isExpressionWithTypeArguments(type) &&
|
|
32
28
|
ts.isIdentifier(type.expression) &&
|
|
33
|
-
wrecNames.has(type.expression.text)
|
|
34
|
-
)
|
|
35
|
-
)
|
|
29
|
+
wrecNames.has(type.expression.text),
|
|
30
|
+
),
|
|
31
|
+
),
|
|
36
32
|
);
|
|
37
33
|
}
|
|
38
34
|
|
|
39
35
|
// Gets a plain string member name when one can be statically determined.
|
|
40
36
|
export function getMemberName(node) {
|
|
41
|
-
const {name} = node;
|
|
37
|
+
const { name } = node;
|
|
42
38
|
return name ? (getNameText(name) ?? undefined) : undefined;
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
// Gets the text value of an AST name node.
|
|
46
42
|
export function getNameText(name) {
|
|
47
|
-
return ts.isIdentifier(name) ||
|
|
48
|
-
ts.isStringLiteral(name) ||
|
|
49
|
-
ts.isPrivateIdentifier(name)
|
|
43
|
+
return ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isPrivateIdentifier(name)
|
|
50
44
|
? name.text
|
|
51
45
|
: null;
|
|
52
46
|
}
|
|
@@ -55,13 +49,13 @@ export function getNameText(name) {
|
|
|
55
49
|
export function getPropertyAssignmentNames(objectLiteral) {
|
|
56
50
|
return objectLiteral.properties
|
|
57
51
|
.filter(ts.isPropertyAssignment)
|
|
58
|
-
.map(property => getMemberName(property))
|
|
59
|
-
.filter(name => name !== undefined);
|
|
52
|
+
.map((property) => getMemberName(property))
|
|
53
|
+
.filter((name) => name !== undefined);
|
|
60
54
|
}
|
|
61
55
|
|
|
62
56
|
// Collects imported Wrec class names and the quote style used for those imports.
|
|
63
57
|
export function getWrecImportInfo(sourceFile) {
|
|
64
|
-
const names = new Set([
|
|
58
|
+
const names = new Set(["Wrec"]);
|
|
65
59
|
let quote = "'";
|
|
66
60
|
|
|
67
61
|
for (const statement of sourceFile.statements) {
|
|
@@ -75,10 +69,10 @@ export function getWrecImportInfo(sourceFile) {
|
|
|
75
69
|
|
|
76
70
|
const moduleName = statement.moduleSpecifier.text;
|
|
77
71
|
const isWrecModule =
|
|
78
|
-
moduleName ===
|
|
79
|
-
moduleName.endsWith(
|
|
80
|
-
moduleName.endsWith(
|
|
81
|
-
moduleName.endsWith(
|
|
72
|
+
moduleName === "wrec" ||
|
|
73
|
+
moduleName.endsWith("/wrec") ||
|
|
74
|
+
moduleName.endsWith("/wrec.js") ||
|
|
75
|
+
moduleName.endsWith("/wrec.ts");
|
|
82
76
|
if (!isWrecModule) continue;
|
|
83
77
|
|
|
84
78
|
const namedBindings = statement.importClause.namedBindings;
|
|
@@ -86,7 +80,7 @@ export function getWrecImportInfo(sourceFile) {
|
|
|
86
80
|
|
|
87
81
|
for (const element of namedBindings.elements) {
|
|
88
82
|
const importedName = element.propertyName?.text ?? element.name.text;
|
|
89
|
-
if (importedName ===
|
|
83
|
+
if (importedName === "Wrec") {
|
|
90
84
|
names.add(element.name.text);
|
|
91
85
|
const moduleText = statement.moduleSpecifier.getText(sourceFile);
|
|
92
86
|
quote = moduleText[0];
|
|
@@ -94,14 +88,12 @@ export function getWrecImportInfo(sourceFile) {
|
|
|
94
88
|
}
|
|
95
89
|
}
|
|
96
90
|
|
|
97
|
-
return {names, quote};
|
|
91
|
+
return { names, quote };
|
|
98
92
|
}
|
|
99
93
|
|
|
100
94
|
// Determines if an AST node has the static modifier.
|
|
101
95
|
export function hasStaticModifier(node) {
|
|
102
96
|
return ts.canHaveModifiers(node)
|
|
103
|
-
? ts
|
|
104
|
-
.getModifiers(node)
|
|
105
|
-
?.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword)
|
|
97
|
+
? ts.getModifiers(node)?.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)
|
|
106
98
|
: false;
|
|
107
99
|
}
|
package/scripts/declare.js
CHANGED
|
@@ -9,22 +9,18 @@
|
|
|
9
9
|
// Include the --dry flag to report whether changes are needed
|
|
10
10
|
// without writing the modified source back to the file.
|
|
11
11
|
|
|
12
|
-
import fs from
|
|
13
|
-
import path from
|
|
14
|
-
import ts from
|
|
15
|
-
import {
|
|
16
|
-
collectWrecClasses,
|
|
17
|
-
getMemberName,
|
|
18
|
-
hasStaticModifier
|
|
19
|
-
} from './ast-utils.js';
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import ts from "typescript";
|
|
15
|
+
import { collectWrecClasses, getMemberName, hasStaticModifier } from "./ast-utils.js";
|
|
20
16
|
|
|
21
17
|
const cwd = process.cwd();
|
|
22
18
|
const DECLARE_TYPE_MAP = new Map([
|
|
23
|
-
[
|
|
24
|
-
[
|
|
25
|
-
[
|
|
26
|
-
[
|
|
27
|
-
[
|
|
19
|
+
["Array", "unknown[]"],
|
|
20
|
+
["Boolean", "boolean"],
|
|
21
|
+
["Number", "number"],
|
|
22
|
+
["Object", "object"],
|
|
23
|
+
["String", "string"],
|
|
28
24
|
]);
|
|
29
25
|
|
|
30
26
|
// Analyzes a parsed source file and returns any proposed `declare` edits.
|
|
@@ -49,49 +45,39 @@ function analyzeSourceFile(sourceFile) {
|
|
|
49
45
|
if (!declareType) continue;
|
|
50
46
|
declareLines.push(createDeclareLine(propName, declareType));
|
|
51
47
|
}
|
|
48
|
+
declareLines.sort((a, b) => a.localeCompare(b));
|
|
52
49
|
|
|
53
50
|
const start = propertiesMember.end;
|
|
54
51
|
const end = findDeclareBlockEnd(classNode, propertiesMember, sourceFile);
|
|
55
|
-
const nextText = buildDeclareBlock(
|
|
56
|
-
sourceFile,
|
|
57
|
-
classNode,
|
|
58
|
-
propertiesMember,
|
|
59
|
-
declareLines
|
|
60
|
-
);
|
|
52
|
+
const nextText = buildDeclareBlock(sourceFile, classNode, propertiesMember, declareLines);
|
|
61
53
|
const currentText = sourceFile.text.slice(start, end);
|
|
62
54
|
|
|
63
55
|
if (nextText !== currentText) {
|
|
64
|
-
edits.push({end, start, text: nextText});
|
|
56
|
+
edits.push({ end, start, text: nextText });
|
|
65
57
|
}
|
|
66
58
|
}
|
|
67
59
|
|
|
68
60
|
if (classNodes.length === 0) {
|
|
69
|
-
return {changed: false, foundWrecSubclass: false, text: sourceFile.text};
|
|
61
|
+
return { changed: false, foundWrecSubclass: false, text: sourceFile.text };
|
|
70
62
|
}
|
|
71
63
|
|
|
72
64
|
if (edits.length === 0) {
|
|
73
|
-
return {changed: false, foundWrecSubclass: true, text: sourceFile.text};
|
|
65
|
+
return { changed: false, foundWrecSubclass: true, text: sourceFile.text };
|
|
74
66
|
}
|
|
75
67
|
|
|
76
68
|
let nextSource = sourceFile.text;
|
|
77
69
|
edits.sort((a, b) => b.start - a.start);
|
|
78
70
|
|
|
79
71
|
for (const edit of edits) {
|
|
80
|
-
nextSource =
|
|
81
|
-
nextSource.slice(0, edit.start) + edit.text + nextSource.slice(edit.end);
|
|
72
|
+
nextSource = nextSource.slice(0, edit.start) + edit.text + nextSource.slice(edit.end);
|
|
82
73
|
}
|
|
83
74
|
|
|
84
|
-
return {changed: true, foundWrecSubclass: true, text: nextSource};
|
|
75
|
+
return { changed: true, foundWrecSubclass: true, text: nextSource };
|
|
85
76
|
}
|
|
86
77
|
|
|
87
78
|
// Builds the `declare` block that should appear after `static properties`.
|
|
88
|
-
function buildDeclareBlock(
|
|
89
|
-
sourceFile
|
|
90
|
-
classNode,
|
|
91
|
-
propertiesMember,
|
|
92
|
-
declareLines
|
|
93
|
-
) {
|
|
94
|
-
const {text} = sourceFile;
|
|
79
|
+
function buildDeclareBlock(sourceFile, classNode, propertiesMember, declareLines) {
|
|
80
|
+
const { text } = sourceFile;
|
|
95
81
|
const memberIndent = getIndent(text, propertiesMember.getStart(sourceFile));
|
|
96
82
|
const startIndex = classNode.members.indexOf(propertiesMember) + 1;
|
|
97
83
|
let nextMember = null;
|
|
@@ -104,15 +90,13 @@ function buildDeclareBlock(
|
|
|
104
90
|
break;
|
|
105
91
|
}
|
|
106
92
|
|
|
107
|
-
const nextIndent = nextMember
|
|
108
|
-
? getIndent(text, nextMember.getStart(sourceFile))
|
|
109
|
-
: '';
|
|
93
|
+
const nextIndent = nextMember ? getIndent(text, nextMember.getStart(sourceFile)) : "";
|
|
110
94
|
|
|
111
95
|
if (declareLines.length === 0) {
|
|
112
|
-
return nextMember ? `\n\n${nextIndent}` :
|
|
96
|
+
return nextMember ? `\n\n${nextIndent}` : "\n";
|
|
113
97
|
}
|
|
114
98
|
|
|
115
|
-
const content = declareLines.map(line => `${memberIndent}${line}`).join(
|
|
99
|
+
const content = declareLines.map((line) => `${memberIndent}${line}`).join("\n");
|
|
116
100
|
return nextMember ? `\n${content}\n\n${nextIndent}` : `\n${content}\n`;
|
|
117
101
|
}
|
|
118
102
|
|
|
@@ -123,15 +107,15 @@ function createDeclareLine(propName, declareType) {
|
|
|
123
107
|
|
|
124
108
|
// Determines what changes, if any, should be made in a source file.
|
|
125
109
|
export function evaluateSourceFile(filePath, options = {}) {
|
|
126
|
-
const {dry = false} = options;
|
|
110
|
+
const { dry = false } = options;
|
|
127
111
|
const absFilePath = path.resolve(cwd, filePath);
|
|
128
112
|
validateFile(absFilePath);
|
|
129
113
|
|
|
130
|
-
const text = fs.readFileSync(absFilePath,
|
|
114
|
+
const text = fs.readFileSync(absFilePath, "utf8");
|
|
131
115
|
const result = evaluateSourceText(absFilePath, text);
|
|
132
116
|
|
|
133
117
|
if (!result.foundWrecSubclass) {
|
|
134
|
-
throw new Error(
|
|
118
|
+
throw new Error("No class extending Wrec was found.");
|
|
135
119
|
}
|
|
136
120
|
|
|
137
121
|
if (!dry && result.changed) {
|
|
@@ -148,7 +132,7 @@ export function evaluateSourceText(filePath, text) {
|
|
|
148
132
|
text,
|
|
149
133
|
ts.ScriptTarget.Latest,
|
|
150
134
|
true,
|
|
151
|
-
ts.ScriptKind.TS
|
|
135
|
+
ts.ScriptKind.TS,
|
|
152
136
|
);
|
|
153
137
|
return analyzeSourceFile(sourceFile);
|
|
154
138
|
}
|
|
@@ -173,7 +157,7 @@ function getDeclareType(initializer) {
|
|
|
173
157
|
for (const property of initializer.properties) {
|
|
174
158
|
if (
|
|
175
159
|
!ts.isPropertyAssignment(property) ||
|
|
176
|
-
getMemberName(property) !==
|
|
160
|
+
getMemberName(property) !== "type" ||
|
|
177
161
|
!ts.isIdentifier(property.initializer)
|
|
178
162
|
) {
|
|
179
163
|
continue;
|
|
@@ -187,9 +171,9 @@ function getDeclareType(initializer) {
|
|
|
187
171
|
|
|
188
172
|
// Gets the leading indentation in the line that contains a given position.
|
|
189
173
|
function getIndent(text, pos) {
|
|
190
|
-
const lineStart = text.lastIndexOf(
|
|
174
|
+
const lineStart = text.lastIndexOf("\n", pos - 1) + 1;
|
|
191
175
|
const match = /^[ \t]*/.exec(text.slice(lineStart));
|
|
192
|
-
return match ? match[0] :
|
|
176
|
+
return match ? match[0] : "";
|
|
193
177
|
}
|
|
194
178
|
|
|
195
179
|
// Gets the last `static properties = { ... }` declaration in a class.
|
|
@@ -200,7 +184,7 @@ function getLastPropertiesDeclaration(classNode) {
|
|
|
200
184
|
if (
|
|
201
185
|
ts.isPropertyDeclaration(member) &&
|
|
202
186
|
hasStaticModifier(member) &&
|
|
203
|
-
getMemberName(member) ===
|
|
187
|
+
getMemberName(member) === "properties" &&
|
|
204
188
|
member.initializer &&
|
|
205
189
|
ts.isObjectLiteralExpression(member.initializer)
|
|
206
190
|
) {
|
|
@@ -216,52 +200,48 @@ function isDeclarePropertyDeclaration(member) {
|
|
|
216
200
|
return (
|
|
217
201
|
ts.isPropertyDeclaration(member) &&
|
|
218
202
|
ts.canHaveModifiers(member) &&
|
|
219
|
-
ts
|
|
220
|
-
.getModifiers(member)
|
|
221
|
-
?.some(mod => mod.kind === ts.SyntaxKind.DeclareKeyword) === true
|
|
203
|
+
ts.getModifiers(member)?.some((mod) => mod.kind === ts.SyntaxKind.DeclareKeyword) === true
|
|
222
204
|
);
|
|
223
205
|
}
|
|
224
206
|
|
|
225
207
|
// Handles CLI arguments and runs the script.
|
|
226
208
|
function main() {
|
|
227
209
|
const args = process.argv.slice(2);
|
|
228
|
-
const unknownFlags = args.filter(
|
|
229
|
-
arg => arg.startsWith('--') && arg !== '--dry'
|
|
230
|
-
);
|
|
210
|
+
const unknownFlags = args.filter((arg) => arg.startsWith("--") && arg !== "--dry");
|
|
231
211
|
if (unknownFlags.length > 0) {
|
|
232
212
|
throw new Error(`unknown option: ${unknownFlags[0]}`);
|
|
233
213
|
}
|
|
234
214
|
|
|
235
|
-
const inputPaths = args.filter(arg => !arg.startsWith(
|
|
215
|
+
const inputPaths = args.filter((arg) => !arg.startsWith("--"));
|
|
236
216
|
if (inputPaths.length !== 1) {
|
|
237
|
-
throw new Error(
|
|
217
|
+
throw new Error("Specify a single source file");
|
|
238
218
|
}
|
|
239
219
|
|
|
240
|
-
const dry = args.includes(
|
|
241
|
-
const result = evaluateSourceFile(inputPaths[0], {dry});
|
|
220
|
+
const dry = args.includes("--dry");
|
|
221
|
+
const result = evaluateSourceFile(inputPaths[0], { dry });
|
|
242
222
|
|
|
243
223
|
if (dry) {
|
|
244
224
|
if (result.changed) process.exit(1);
|
|
245
|
-
console.info(
|
|
225
|
+
console.info("no changes needed");
|
|
246
226
|
return;
|
|
247
227
|
}
|
|
248
228
|
|
|
249
229
|
if (result.changed) {
|
|
250
|
-
console.info(
|
|
230
|
+
console.info("updated source file");
|
|
251
231
|
} else {
|
|
252
|
-
console.info(
|
|
232
|
+
console.info("no changes needed");
|
|
253
233
|
}
|
|
254
234
|
}
|
|
255
235
|
|
|
256
236
|
// Validates that a source file exists and has a supported extension.
|
|
257
237
|
function validateFile(absFilePath) {
|
|
258
|
-
if (!fs.existsSync(absFilePath)) throw new Error(
|
|
238
|
+
if (!fs.existsSync(absFilePath)) throw new Error("File not found");
|
|
259
239
|
|
|
260
240
|
const stat = fs.statSync(absFilePath);
|
|
261
|
-
if (!stat.isFile()) throw new Error(
|
|
241
|
+
if (!stat.isFile()) throw new Error("Not a file");
|
|
262
242
|
|
|
263
|
-
if (
|
|
264
|
-
throw new Error(
|
|
243
|
+
if (!absFilePath.endsWith(".ts")) {
|
|
244
|
+
throw new Error("declare statements can only be added in .ts files");
|
|
265
245
|
}
|
|
266
246
|
}
|
|
267
247
|
|