@postxl/generator 1.4.0 → 1.4.1
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/dist/utils/custom-blocks.d.ts +2 -0
- package/dist/utils/custom-blocks.js +160 -19
- package/package.json +2 -2
|
@@ -65,6 +65,8 @@ export type CustomBlock = {
|
|
|
65
65
|
anchorBefore: string[];
|
|
66
66
|
/** Anchor context: non-empty lines after the block for positioning */
|
|
67
67
|
anchorAfter: string[];
|
|
68
|
+
/** Name of the enclosing class member/method where this block was extracted */
|
|
69
|
+
enclosingMemberName?: string;
|
|
68
70
|
};
|
|
69
71
|
/**
|
|
70
72
|
* Result of extracting custom blocks from source code
|
|
@@ -119,14 +119,17 @@ function processEndMarker(line, lineIndex, endMatch, currentBlock, lines, blocks
|
|
|
119
119
|
// Capture anchor context
|
|
120
120
|
const anchorBefore = captureAnchorContext(lines, currentBlock.startLineIndex, 'before');
|
|
121
121
|
const anchorAfter = captureAnchorContext(lines, lineIndex, 'after');
|
|
122
|
-
|
|
122
|
+
const enclosingMemberName = findEnclosingMemberName(lines, currentBlock.startLineIndex);
|
|
123
|
+
const block = {
|
|
123
124
|
name: currentBlock.name,
|
|
124
125
|
lines: currentBlock.lines,
|
|
125
126
|
startLineIndex: currentBlock.startLineIndex,
|
|
126
127
|
endLineIndex: lineIndex,
|
|
127
128
|
anchorBefore,
|
|
128
129
|
anchorAfter,
|
|
129
|
-
|
|
130
|
+
...(enclosingMemberName ? { enclosingMemberName } : {}),
|
|
131
|
+
};
|
|
132
|
+
blocks.push(block);
|
|
130
133
|
return null;
|
|
131
134
|
}
|
|
132
135
|
/**
|
|
@@ -246,6 +249,147 @@ function isSignificantLine(line) {
|
|
|
246
249
|
function normalizeAnchorLine(line) {
|
|
247
250
|
return line.trim();
|
|
248
251
|
}
|
|
252
|
+
const METHOD_SIGNATURE_PATTERN = /^\s*(?:public|private|protected|static|readonly|async|\s)*([A-Za-z_$][\w$]*)\s*(?:<[^>{}]*>)?\s*\(/;
|
|
253
|
+
const NON_MEMBER_KEYWORDS = new Set([
|
|
254
|
+
'if',
|
|
255
|
+
'for',
|
|
256
|
+
'while',
|
|
257
|
+
'switch',
|
|
258
|
+
'do',
|
|
259
|
+
'return',
|
|
260
|
+
'const',
|
|
261
|
+
'let',
|
|
262
|
+
'var',
|
|
263
|
+
'new',
|
|
264
|
+
'class',
|
|
265
|
+
'function',
|
|
266
|
+
'typeof',
|
|
267
|
+
'instanceof',
|
|
268
|
+
]);
|
|
269
|
+
function findMemberNameInLine(line) {
|
|
270
|
+
const trimmed = line.trim();
|
|
271
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*')) {
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
const signatureMatch = METHOD_SIGNATURE_PATTERN.exec(line);
|
|
275
|
+
if (!signatureMatch) {
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
const memberName = signatureMatch[1];
|
|
279
|
+
if (!memberName || NON_MEMBER_KEYWORDS.has(memberName)) {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
return memberName;
|
|
283
|
+
}
|
|
284
|
+
function findEnclosingMemberName(lines, blockStartLineIndex) {
|
|
285
|
+
for (let i = blockStartLineIndex - 1; i >= 0; i--) {
|
|
286
|
+
const line = lines[i];
|
|
287
|
+
if (line === undefined) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
const memberName = findMemberNameInLine(line);
|
|
291
|
+
if (memberName) {
|
|
292
|
+
return memberName;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
function inferMemberNameFromBlockName(blockName) {
|
|
298
|
+
if (!blockName) {
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
const separatorMatch = /[_-]([A-Za-z_$][\w$]*)$/.exec(blockName);
|
|
302
|
+
if (!separatorMatch) {
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
return separatorMatch[1];
|
|
306
|
+
}
|
|
307
|
+
function countChar(line, char) {
|
|
308
|
+
let count = 0;
|
|
309
|
+
let inSingleQuote = false;
|
|
310
|
+
let inDoubleQuote = false;
|
|
311
|
+
let inTemplate = false;
|
|
312
|
+
let isEscaped = false;
|
|
313
|
+
for (const lineChar of line) {
|
|
314
|
+
if (isEscaped) {
|
|
315
|
+
isEscaped = false;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (lineChar === '\\') {
|
|
319
|
+
isEscaped = true;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (!inDoubleQuote && !inTemplate && lineChar === "'") {
|
|
323
|
+
inSingleQuote = !inSingleQuote;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (!inSingleQuote && !inTemplate && lineChar === '"') {
|
|
327
|
+
inDoubleQuote = !inDoubleQuote;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (!inSingleQuote && !inDoubleQuote && lineChar === '`') {
|
|
331
|
+
inTemplate = !inTemplate;
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (inSingleQuote || inDoubleQuote || inTemplate) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (lineChar === char) {
|
|
338
|
+
count++;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return count;
|
|
342
|
+
}
|
|
343
|
+
function findMemberRange(targetLines, memberName) {
|
|
344
|
+
for (let i = 0; i < targetLines.length; i++) {
|
|
345
|
+
const line = targetLines[i];
|
|
346
|
+
if (line === undefined) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (findMemberNameInLine(line) !== memberName) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
let j = i;
|
|
353
|
+
let foundOpeningBrace = line.includes('{');
|
|
354
|
+
while (!foundOpeningBrace && j + 1 < targetLines.length) {
|
|
355
|
+
j++;
|
|
356
|
+
const continuationLine = targetLines[j];
|
|
357
|
+
if (continuationLine?.includes('{')) {
|
|
358
|
+
foundOpeningBrace = true;
|
|
359
|
+
}
|
|
360
|
+
if (continuationLine?.trim().endsWith(';')) {
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (!foundOpeningBrace) {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
let braceDepth = 0;
|
|
368
|
+
for (let k = i; k < targetLines.length; k++) {
|
|
369
|
+
const memberLine = targetLines[k];
|
|
370
|
+
if (memberLine === undefined) {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
braceDepth += countChar(memberLine, '{');
|
|
374
|
+
braceDepth -= countChar(memberLine, '}');
|
|
375
|
+
if (braceDepth === 0 && k > i) {
|
|
376
|
+
return { start: i, end: k + 1 };
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
function findInsertionPositionByAnchors(block, targetLines) {
|
|
383
|
+
const beforePosition = findAnchorSequence(block.anchorBefore, targetLines, 'before');
|
|
384
|
+
if (beforePosition !== null) {
|
|
385
|
+
return beforePosition + 1;
|
|
386
|
+
}
|
|
387
|
+
const afterPosition = findAnchorSequence(block.anchorAfter, targetLines, 'after');
|
|
388
|
+
if (afterPosition !== null) {
|
|
389
|
+
return afterPosition;
|
|
390
|
+
}
|
|
391
|
+
return findSingleAnchorMatch(block, targetLines);
|
|
392
|
+
}
|
|
249
393
|
/**
|
|
250
394
|
* Finds the best position to insert a custom block in the target content
|
|
251
395
|
* based on anchor context matching.
|
|
@@ -255,24 +399,21 @@ function normalizeAnchorLine(line) {
|
|
|
255
399
|
* @returns The line index where the block should be inserted, or null if no good position found
|
|
256
400
|
*/
|
|
257
401
|
function findInsertionPosition(block, targetLines) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const singleAnchor = findSingleAnchorMatch(block, targetLines);
|
|
272
|
-
if (singleAnchor !== null) {
|
|
273
|
-
return singleAnchor;
|
|
402
|
+
const candidateMemberNames = [
|
|
403
|
+
...new Set([block.enclosingMemberName, inferMemberNameFromBlockName(block.name)].filter((name) => Boolean(name))),
|
|
404
|
+
];
|
|
405
|
+
for (const memberName of candidateMemberNames) {
|
|
406
|
+
const range = findMemberRange(targetLines, memberName);
|
|
407
|
+
if (!range) {
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
const scopedLines = targetLines.slice(range.start, range.end);
|
|
411
|
+
const scopedPosition = findInsertionPositionByAnchors(block, scopedLines);
|
|
412
|
+
if (scopedPosition !== null) {
|
|
413
|
+
return range.start + scopedPosition;
|
|
414
|
+
}
|
|
274
415
|
}
|
|
275
|
-
return
|
|
416
|
+
return findInsertionPositionByAnchors(block, targetLines);
|
|
276
417
|
}
|
|
277
418
|
/**
|
|
278
419
|
* Finds a sequence of anchor lines in the target content
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@postxl/generator",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Core package that orchestrates the code generation of a PXL project",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"jszip": "3.10.1",
|
|
47
47
|
"minimatch": "^10.2.2",
|
|
48
48
|
"p-limit": "3.1.0",
|
|
49
|
-
"@postxl/schema": "^1.
|
|
49
|
+
"@postxl/schema": "^1.10.1",
|
|
50
50
|
"@postxl/utils": "^1.4.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|