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 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 'wrec';
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('my-counter');
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: ['red', 'yellow', 'green'],
123
- value: 'red'
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('demo');
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 = 'Earth'; // to modify "name" property
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.#te(this.shadowRoot), this.#M(this.shadowRoot), this.#Y(), this.#m(), this.ready();
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
- this.#$(t, s, e), e = this.#j(t, s, e), this.#R(u, e);
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.#te(e), this.#M(e);
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.#ee();
996
+ this.#te();
997
997
  continue;
998
998
  }
999
999
  if (!t.has(e.getPropName(n))) {
1000
1000
  if (n === "name") {
1001
- this.#ee();
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
- #te(e) {
1049
+ #ne(e) {
1041
1050
  let t = Array.from(e.querySelectorAll("*"));
1042
1051
  for (let e of t) {
1043
1052
  let t = [];
@@ -19,6 +19,7 @@ declare type PropertyConfig<T = any> = {
19
19
  required?: boolean;
20
20
  type: PropertyType;
21
21
  usedBy?: string | string[];
22
+ validate?: (value: T) => string | void;
22
23
  value?: T;
23
24
  values?: T extends string ? string[] : never;
24
25
  };
@@ -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-DHGadgxK.js";
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
@@ -19,6 +19,7 @@ declare type PropertyConfig<T = any> = {
19
19
  required?: boolean;
20
20
  type: PropertyType;
21
21
  usedBy?: string | string[];
22
+ validate?: (value: T) => string | void;
22
23
  value?: T;
23
24
  values?: T extends string ? string[] : never;
24
25
  };
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-DHGadgxK.js";
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
- "author": "R. Mark Volkmann",
5
- "version": "0.40.2",
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
- "keywords": [
12
- "web",
13
- "component"
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
- "bin": {
32
- "wrec-declare": "./scripts/declare.js",
33
- "wrec-lint": "./scripts/lint.js",
34
- "wrec-scaffold": "./scripts/scaffold.js",
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
- "files": [
38
- "dist",
39
- "scripts/ast-utils.js",
40
- "scripts/declare.js",
41
- "scripts/lint.js",
42
- "scripts/scaffold.js",
43
- "scripts/template.ts",
44
- "scripts/used-by.js",
45
- "README.md"
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
+ }
@@ -1,16 +1,12 @@
1
- import ts from 'typescript';
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(['Wrec']);
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 === 'wrec' ||
79
- moduleName.endsWith('/wrec') ||
80
- moduleName.endsWith('/wrec.js') ||
81
- moduleName.endsWith('/wrec.ts');
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 === 'Wrec') {
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
  }
@@ -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 'node:fs';
13
- import path from 'node:path';
14
- import ts from 'typescript';
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
- ['Array', 'unknown[]'],
24
- ['Boolean', 'boolean'],
25
- ['Number', 'number'],
26
- ['Object', 'object'],
27
- ['String', 'string']
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}` : '\n';
96
+ return nextMember ? `\n\n${nextIndent}` : "\n";
113
97
  }
114
98
 
115
- const content = declareLines.map(line => `${memberIndent}${line}`).join('\n');
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, 'utf8');
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('No class extending Wrec was found.');
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) !== 'type' ||
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('\n', pos - 1) + 1;
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) === 'properties' &&
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('Specify a single source file');
217
+ throw new Error("Specify a single source file");
238
218
  }
239
219
 
240
- const dry = args.includes('--dry');
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('no changes needed');
225
+ console.info("no changes needed");
246
226
  return;
247
227
  }
248
228
 
249
229
  if (result.changed) {
250
- console.info('updated source file');
230
+ console.info("updated source file");
251
231
  } else {
252
- console.info('no changes needed');
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('File not found');
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('Not a file');
241
+ if (!stat.isFile()) throw new Error("Not a file");
262
242
 
263
- if (!/\.ts$/.test(absFilePath)) {
264
- throw new Error('declare statements can only be added in .ts files');
243
+ if (!absFilePath.endsWith(".ts")) {
244
+ throw new Error("declare statements can only be added in .ts files");
265
245
  }
266
246
  }
267
247