@webjsdev/server 0.8.52 → 0.8.53
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/package.json +1 -1
- package/src/check.js +42 -46
package/package.json
CHANGED
package/src/check.js
CHANGED
|
@@ -235,74 +235,70 @@ function isArrayTypeText(type) {
|
|
|
235
235
|
function findFieldInitializers(classBody, props) {
|
|
236
236
|
/** @type {string[]} */
|
|
237
237
|
const out = [];
|
|
238
|
-
|
|
239
|
-
// class-field
|
|
238
|
+
const n = classBody.length;
|
|
239
|
+
// Match class-field declarations. Two shapes:
|
|
240
|
+
// 1. With initializer: `count = 0`, `count: number = 0`, `public count = 0`
|
|
241
|
+
// 2. Type-only (no initializer): `count!: number`, `count?: number`, `count: number`
|
|
242
|
+
// Both compile to Object.defineProperty after super() under modern class-field
|
|
243
|
+
// semantics, clobbering the reactive accessor.
|
|
244
|
+
// The initializer regex: optional modifier, identifier, optional type, then `=`.
|
|
245
|
+
const initRe = /^\s*(?:(public|private|protected|readonly)\s+)?([A-Za-z_$][\w$]*)\s*(?::\s*[^=;]+)?\s*=\s*[^=>]/;
|
|
246
|
+
// The type-only regex: optional modifier, identifier, then `!:` or `?:` or `:` with a type.
|
|
247
|
+
const typeOnlyRe = /^\s*(?:(public|private|protected|readonly)\s+)?([A-Za-z_$][\w$]*)\s*[!?]?\s*:\s*\S/;
|
|
248
|
+
const examineLine = (lineStart) => {
|
|
249
|
+
let j = lineStart;
|
|
250
|
+
while (j < n && classBody[j] !== '\n') j++;
|
|
251
|
+
const line = classBody.slice(lineStart, j);
|
|
252
|
+
const initM = initRe.exec(line);
|
|
253
|
+
const typeM = typeOnlyRe.exec(line);
|
|
254
|
+
// Prefer the initializer match; if neither, skip.
|
|
255
|
+
const name = initM ? initM[2] : (typeM ? typeM[2] : null);
|
|
256
|
+
// `declare`, `static`, and `this.` patterns shouldn't reach here
|
|
257
|
+
// (declare/static start with their keyword, this.x has the dot in
|
|
258
|
+
// the regex group), but guard against matching keywords as names:
|
|
259
|
+
if (name && name !== 'declare' && name !== 'static' && props.has(name)) out.push(name);
|
|
260
|
+
};
|
|
261
|
+
// Walk the body char by char, tracking brace depth. A class field lives at
|
|
262
|
+
// the class-body top level (depth 0). We examine a line ONCE, at its first
|
|
263
|
+
// non-whitespace char, only while depth is 0, and WITHOUT skipping the braces
|
|
264
|
+
// on that line: an opening brace on the line (`method() {`, `field = {`) must
|
|
265
|
+
// still be counted so the lines inside its block are seen at depth > 0. The
|
|
266
|
+
// earlier version jumped `i` past the whole examined line, which dropped that
|
|
267
|
+
// line's braces and let object-literal keys inside a method body (`{ game:
|
|
268
|
+
// ..., scoreboard: ... }`) be misread as depth-0 class fields (#934).
|
|
240
269
|
let depth = 0;
|
|
241
270
|
let i = 0;
|
|
242
271
|
let lineStart = 0;
|
|
272
|
+
let examined = false;
|
|
243
273
|
let str = '';
|
|
244
|
-
while (i <
|
|
274
|
+
while (i < n) {
|
|
245
275
|
const c = classBody[i];
|
|
246
276
|
if (str) {
|
|
247
277
|
if (c === '\\') { i += 2; continue; }
|
|
248
278
|
if (c === str) str = '';
|
|
249
|
-
else if (str === '`' && c === '$' && classBody[i + 1] === '{') {
|
|
250
|
-
depth++;
|
|
251
|
-
i += 2;
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
i++;
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
if (c === '\n') {
|
|
258
|
-
lineStart = i + 1;
|
|
259
279
|
i++;
|
|
260
280
|
continue;
|
|
261
281
|
}
|
|
282
|
+
if (c === '\n') { lineStart = i + 1; examined = false; i++; continue; }
|
|
262
283
|
if (c === '/' && classBody[i + 1] === '/') {
|
|
263
|
-
while (i <
|
|
284
|
+
while (i < n && classBody[i] !== '\n') i++;
|
|
264
285
|
continue;
|
|
265
286
|
}
|
|
266
287
|
if (c === '/' && classBody[i + 1] === '*') {
|
|
267
288
|
i += 2;
|
|
268
|
-
while (i <
|
|
289
|
+
while (i < n && !(classBody[i] === '*' && classBody[i + 1] === '/')) i++;
|
|
269
290
|
i += 2;
|
|
270
291
|
continue;
|
|
271
292
|
}
|
|
272
293
|
if (c === "'" || c === '"' || c === '`') { str = c; i++; continue; }
|
|
294
|
+
// First non-whitespace char of a class-body top-level line: examine it for
|
|
295
|
+
// a field declaration BEFORE consuming any brace it opens.
|
|
296
|
+
if (depth === 0 && !examined && !/\s/.test(c)) {
|
|
297
|
+
examined = true;
|
|
298
|
+
examineLine(lineStart);
|
|
299
|
+
}
|
|
273
300
|
if (c === '{') { depth++; i++; continue; }
|
|
274
301
|
if (c === '}') { depth--; i++; continue; }
|
|
275
|
-
// At class-body top level, examine candidate lines starting at lineStart.
|
|
276
|
-
if (depth === 0 && i === lineStart || (depth === 0 && /\s/.test(c) && i === lineStart)) {
|
|
277
|
-
// Take the rest of the line up to a newline.
|
|
278
|
-
let j = lineStart;
|
|
279
|
-
while (j < classBody.length && classBody[j] !== '\n') j++;
|
|
280
|
-
const line = classBody.slice(lineStart, j);
|
|
281
|
-
// Match class-field declarations. Two shapes:
|
|
282
|
-
// 1. With initializer: `count = 0`, `count: number = 0`, `public count = 0`
|
|
283
|
-
// 2. Type-only (no initializer): `count!: number`, `count?: number`, `count: number`
|
|
284
|
-
// Both compile to Object.defineProperty after super() under modern class-field
|
|
285
|
-
// semantics, clobbering the reactive accessor.
|
|
286
|
-
// The initializer regex: optional modifier, identifier, optional type, then `=`.
|
|
287
|
-
const initRe = /^\s*(?:(public|private|protected|readonly)\s+)?([A-Za-z_$][\w$]*)\s*(?::\s*[^=;]+)?\s*=\s*[^=>]/;
|
|
288
|
-
// The type-only regex: optional modifier, identifier, then `!:` or `?:` or `:` with a type.
|
|
289
|
-
const typeOnlyRe = /^\s*(?:(public|private|protected|readonly)\s+)?([A-Za-z_$][\w$]*)\s*[!?]?\s*:\s*\S/;
|
|
290
|
-
const initM = initRe.exec(line);
|
|
291
|
-
const typeM = typeOnlyRe.exec(line);
|
|
292
|
-
// Prefer the initializer match; if neither, skip.
|
|
293
|
-
const name = initM ? initM[2] : (typeM ? typeM[2] : null);
|
|
294
|
-
if (name) {
|
|
295
|
-
// `declare`, `static`, and `this.` patterns shouldn't reach here
|
|
296
|
-
// (declare/static start with their keyword, this.x has the dot in
|
|
297
|
-
// the regex group), but guard against matching keywords as names:
|
|
298
|
-
if (name !== 'declare' && name !== 'static' && props.has(name)) {
|
|
299
|
-
out.push(name);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
// Advance past this line so we don't re-match.
|
|
303
|
-
i = j;
|
|
304
|
-
continue;
|
|
305
|
-
}
|
|
306
302
|
i++;
|
|
307
303
|
}
|
|
308
304
|
return out;
|