@functional-examples/javascript 0.0.0-alpha.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/extractor.d.ts +15 -0
- package/dist/extractor.d.ts.map +1 -0
- package/dist/extractor.js +589 -0
- package/dist/extractor.js.map +1 -0
- package/dist/frontmatter.d.ts +21 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +145 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +120 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +7 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +59 -0
- package/dist/parser.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Extractor } from '@functional-examples/devkit';
|
|
2
|
+
/**
|
|
3
|
+
* Create a JavaScript/TypeScript extractor.
|
|
4
|
+
*
|
|
5
|
+
* This extractor:
|
|
6
|
+
* - Receives pre-filtered candidates (files and directories)
|
|
7
|
+
* - For directories with package.json: treats as multi-file package example
|
|
8
|
+
* - For files with frontmatter: treats as single-file example
|
|
9
|
+
* - Extracts metadata from package.json or frontmatter
|
|
10
|
+
* - Loads raw content into ExampleFile
|
|
11
|
+
* - Respects exclude patterns (default: node_modules, .git, dist, build)
|
|
12
|
+
* - Tracks claimed files for conflict detection
|
|
13
|
+
*/
|
|
14
|
+
export declare function createJavaScriptExtractor(): Extractor;
|
|
15
|
+
//# sourceMappingURL=extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../src/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,SAAS,EAIV,MAAM,6BAA6B,CAAC;AAigBrC;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,IAAI,SAAS,CAqPrD"}
|
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
import { createMatcher, glob, parseJson, parseYaml, } from '@functional-examples/devkit';
|
|
2
|
+
import dependencyTree from 'dependency-tree';
|
|
3
|
+
import * as fs from 'node:fs/promises';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
const EXTRACTOR_NAME = 'javascript-extractor';
|
|
6
|
+
/** Default patterns to exclude from file scanning */
|
|
7
|
+
const DEFAULT_EXCLUDE_PATTERNS = [
|
|
8
|
+
'**/node_modules/**',
|
|
9
|
+
'**/.git/**',
|
|
10
|
+
'**/dist/**',
|
|
11
|
+
'**/build/**',
|
|
12
|
+
];
|
|
13
|
+
/** Default directories to skip when processing directory candidates */
|
|
14
|
+
const DEFAULT_EXCLUDED_DIRS = new Set([
|
|
15
|
+
'node_modules',
|
|
16
|
+
'.git',
|
|
17
|
+
'dist',
|
|
18
|
+
'build',
|
|
19
|
+
]);
|
|
20
|
+
/** File extensions to scan for JavaScript/TypeScript examples */
|
|
21
|
+
const FILE_PATTERNS = [
|
|
22
|
+
'**/*.ts',
|
|
23
|
+
'**/*.tsx',
|
|
24
|
+
'**/*.js',
|
|
25
|
+
'**/*.jsx',
|
|
26
|
+
'**/*.mjs',
|
|
27
|
+
'**/*.cjs',
|
|
28
|
+
'**/*.mts',
|
|
29
|
+
'**/*.cts',
|
|
30
|
+
];
|
|
31
|
+
/** File extensions this extractor handles */
|
|
32
|
+
const FILE_EXTENSIONS = [
|
|
33
|
+
'.ts',
|
|
34
|
+
'.tsx',
|
|
35
|
+
'.js',
|
|
36
|
+
'.jsx',
|
|
37
|
+
'.mjs',
|
|
38
|
+
'.cjs',
|
|
39
|
+
'.mts',
|
|
40
|
+
'.cts',
|
|
41
|
+
];
|
|
42
|
+
/** Pattern matching line comment frontmatter start: // --- */
|
|
43
|
+
const LINE_COMMENT_START = /^[ \t]*\/\/\s*---\s*$/;
|
|
44
|
+
/** Pattern matching line comment frontmatter end: // --- */
|
|
45
|
+
const LINE_COMMENT_END = /^[ \t]*\/\/\s*---\s*$/;
|
|
46
|
+
/** Pattern matching line comment content: // <content> */
|
|
47
|
+
const LINE_COMMENT_CONTENT = /^[ \t]*\/\/\s?(.*)$/;
|
|
48
|
+
/** Pattern matching block comment frontmatter start: /* --- */
|
|
49
|
+
const BLOCK_COMMENT_START = /^[ \t]*\/\*\s*---\s*$/;
|
|
50
|
+
/** Pattern matching block comment frontmatter end: --- */
|
|
51
|
+
const BLOCK_COMMENT_END = /^[ \t]*---\s*\*\/\s*$/;
|
|
52
|
+
/**
|
|
53
|
+
* Try to extract line comment style frontmatter from the start of the file.
|
|
54
|
+
*/
|
|
55
|
+
async function extractLineCommentFrontmatter(lines) {
|
|
56
|
+
if (lines.length < 2)
|
|
57
|
+
return null;
|
|
58
|
+
if (!LINE_COMMENT_START.test(lines[0])) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const yamlLines = [];
|
|
62
|
+
let endIndex = -1;
|
|
63
|
+
for (let i = 1; i < lines.length; i++) {
|
|
64
|
+
const line = lines[i];
|
|
65
|
+
if (LINE_COMMENT_END.test(line)) {
|
|
66
|
+
endIndex = i;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
const match = line.match(LINE_COMMENT_CONTENT);
|
|
70
|
+
if (match) {
|
|
71
|
+
yamlLines.push(match[1]);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (endIndex === -1) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const yamlContent = yamlLines.join('\n');
|
|
81
|
+
const metadata = yamlContent.trim()
|
|
82
|
+
? (await parseYaml(yamlContent)) ?? {}
|
|
83
|
+
: {};
|
|
84
|
+
return { metadata };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Try to extract block comment style frontmatter from the start of the file.
|
|
88
|
+
*/
|
|
89
|
+
async function extractBlockCommentFrontmatter(lines) {
|
|
90
|
+
if (lines.length < 2)
|
|
91
|
+
return null;
|
|
92
|
+
if (!BLOCK_COMMENT_START.test(lines[0])) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const yamlLines = [];
|
|
96
|
+
let endIndex = -1;
|
|
97
|
+
for (let i = 1; i < lines.length; i++) {
|
|
98
|
+
const line = lines[i];
|
|
99
|
+
if (BLOCK_COMMENT_END.test(line)) {
|
|
100
|
+
endIndex = i;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
yamlLines.push(line);
|
|
104
|
+
}
|
|
105
|
+
if (endIndex === -1) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const yamlContent = yamlLines.join('\n');
|
|
109
|
+
const metadata = yamlContent.trim()
|
|
110
|
+
? (await parseYaml(yamlContent)) ?? {}
|
|
111
|
+
: {};
|
|
112
|
+
return { metadata };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Extract frontmatter from file content.
|
|
116
|
+
*/
|
|
117
|
+
async function extractFrontmatter(content) {
|
|
118
|
+
const lines = content.split('\n');
|
|
119
|
+
const result = (await extractLineCommentFrontmatter(lines)) ??
|
|
120
|
+
(await extractBlockCommentFrontmatter(lines));
|
|
121
|
+
if (!result) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return result.metadata;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if metadata has valid required fields (id and title as strings).
|
|
128
|
+
*/
|
|
129
|
+
function hasValidMetadata(metadata) {
|
|
130
|
+
return (typeof metadata.id === 'string' &&
|
|
131
|
+
metadata.id.length > 0 &&
|
|
132
|
+
typeof metadata.title === 'string' &&
|
|
133
|
+
metadata.title.length > 0);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Strip npm scope from package name.
|
|
137
|
+
* @example "@examples/foo" -> "foo"
|
|
138
|
+
* @example "my-package" -> "my-package"
|
|
139
|
+
*/
|
|
140
|
+
function stripScope(name) {
|
|
141
|
+
if (name.startsWith('@')) {
|
|
142
|
+
const slashIndex = name.indexOf('/');
|
|
143
|
+
if (slashIndex !== -1) {
|
|
144
|
+
return name.slice(slashIndex + 1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return name;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Recursively extract all file paths from exports field.
|
|
151
|
+
* Handles string, object with conditions, and nested export maps.
|
|
152
|
+
*/
|
|
153
|
+
function extractExportPaths(exports) {
|
|
154
|
+
if (!exports) {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
if (typeof exports === 'string') {
|
|
158
|
+
return [exports];
|
|
159
|
+
}
|
|
160
|
+
if (typeof exports === 'object') {
|
|
161
|
+
const paths = [];
|
|
162
|
+
for (const value of Object.values(exports)) {
|
|
163
|
+
paths.push(...extractExportPaths(value));
|
|
164
|
+
}
|
|
165
|
+
return paths;
|
|
166
|
+
}
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Check if a file exists.
|
|
171
|
+
*/
|
|
172
|
+
async function fileExists(filePath) {
|
|
173
|
+
try {
|
|
174
|
+
const stat = await fs.stat(filePath);
|
|
175
|
+
return stat.isFile();
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Read and parse a package.json file.
|
|
183
|
+
* Returns the parsed package, a parse error, or null if the file can't be read.
|
|
184
|
+
*/
|
|
185
|
+
async function readPackageJson(packageJsonPath) {
|
|
186
|
+
let content;
|
|
187
|
+
try {
|
|
188
|
+
content = await fs.readFile(packageJsonPath, 'utf-8');
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
return { pkg: (await parseJson(content, packageJsonPath)) };
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
return {
|
|
198
|
+
error: `Failed to parse package.json: ${e.message}`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Strip the functional-examples key from package.json content for output.
|
|
204
|
+
*/
|
|
205
|
+
function stripFunctionalExamplesKey(packageJsonContent) {
|
|
206
|
+
try {
|
|
207
|
+
const pkg = JSON.parse(packageJsonContent);
|
|
208
|
+
delete pkg['functional-examples'];
|
|
209
|
+
return JSON.stringify(pkg, null, 2);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return packageJsonContent;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Use dependency-tree to trace imports from an entry file.
|
|
217
|
+
* Returns absolute paths of all traced dependencies within the example directory.
|
|
218
|
+
*/
|
|
219
|
+
function traceDependencies(entryPath, exampleDir) {
|
|
220
|
+
try {
|
|
221
|
+
const tree = dependencyTree({
|
|
222
|
+
filename: entryPath,
|
|
223
|
+
directory: exampleDir,
|
|
224
|
+
nodeModulesConfig: {
|
|
225
|
+
// Don't trace into node_modules
|
|
226
|
+
entry: 'module',
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
// Flatten the dependency tree to get all unique file paths
|
|
230
|
+
// Only include files within the example directory
|
|
231
|
+
const files = new Set();
|
|
232
|
+
function collectFiles(node) {
|
|
233
|
+
if (typeof node === 'string') {
|
|
234
|
+
// Only add files within the example directory
|
|
235
|
+
if (node.startsWith(exampleDir) && !node.includes('node_modules')) {
|
|
236
|
+
files.add(node);
|
|
237
|
+
}
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
for (const [filePath, deps] of Object.entries(node)) {
|
|
241
|
+
// Only add files within the example directory
|
|
242
|
+
if (filePath &&
|
|
243
|
+
typeof filePath === 'string' &&
|
|
244
|
+
filePath.startsWith(exampleDir) &&
|
|
245
|
+
!filePath.includes('node_modules')) {
|
|
246
|
+
files.add(filePath);
|
|
247
|
+
}
|
|
248
|
+
if (deps) {
|
|
249
|
+
collectFiles(deps);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
collectFiles(tree);
|
|
254
|
+
return Array.from(files);
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
// If dependency tracing fails, just return the entry file
|
|
258
|
+
return [entryPath];
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Collect files for a multi-file package example.
|
|
263
|
+
*/
|
|
264
|
+
async function collectPackageFiles(exampleDir, pkg, options) {
|
|
265
|
+
const collectedFiles = new Set();
|
|
266
|
+
const packageJsonPath = path.join(exampleDir, 'package.json');
|
|
267
|
+
// Always include package.json
|
|
268
|
+
collectedFiles.add(packageJsonPath);
|
|
269
|
+
// Always include README.md if it exists
|
|
270
|
+
for (const readmeName of ['README.md', 'README', 'readme.md']) {
|
|
271
|
+
const readmePath = path.join(exampleDir, readmeName);
|
|
272
|
+
if (await fileExists(readmePath)) {
|
|
273
|
+
collectedFiles.add(readmePath);
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Collect entry points to trace
|
|
278
|
+
const entryPoints = [];
|
|
279
|
+
// Add main field
|
|
280
|
+
if (pkg.main) {
|
|
281
|
+
const mainPath = path.resolve(exampleDir, pkg.main);
|
|
282
|
+
if (await fileExists(mainPath)) {
|
|
283
|
+
entryPoints.push(mainPath);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Add module field
|
|
287
|
+
if (pkg.module) {
|
|
288
|
+
const modulePath = path.resolve(exampleDir, pkg.module);
|
|
289
|
+
if (await fileExists(modulePath)) {
|
|
290
|
+
entryPoints.push(modulePath);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// Add types field
|
|
294
|
+
if (pkg.types) {
|
|
295
|
+
const typesPath = path.resolve(exampleDir, pkg.types);
|
|
296
|
+
if (await fileExists(typesPath)) {
|
|
297
|
+
entryPoints.push(typesPath);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// Add exports field paths
|
|
301
|
+
if (pkg.exports) {
|
|
302
|
+
const exportPaths = extractExportPaths(pkg.exports);
|
|
303
|
+
for (const exportPath of exportPaths) {
|
|
304
|
+
if (exportPath.startsWith('.')) {
|
|
305
|
+
const resolvedPath = path.resolve(exampleDir, exportPath);
|
|
306
|
+
if (await fileExists(resolvedPath)) {
|
|
307
|
+
entryPoints.push(resolvedPath);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// Trace dependencies from all entry points
|
|
313
|
+
for (const entryPath of entryPoints) {
|
|
314
|
+
const tracedFiles = traceDependencies(entryPath, exampleDir);
|
|
315
|
+
for (const file of tracedFiles) {
|
|
316
|
+
collectedFiles.add(file);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
// Include files from files array using fast-glob
|
|
320
|
+
if (pkg.files && pkg.files.length > 0) {
|
|
321
|
+
try {
|
|
322
|
+
const excludePatterns = [
|
|
323
|
+
...DEFAULT_EXCLUDE_PATTERNS,
|
|
324
|
+
...(options.exclude ?? []),
|
|
325
|
+
];
|
|
326
|
+
const filesFromGlob = await glob(pkg.files, {
|
|
327
|
+
cwd: exampleDir,
|
|
328
|
+
absolute: true,
|
|
329
|
+
ignore: excludePatterns,
|
|
330
|
+
onlyFiles: true,
|
|
331
|
+
});
|
|
332
|
+
for (const file of filesFromGlob) {
|
|
333
|
+
collectedFiles.add(file);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
// Ignore glob errors
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// Check if we have any files besides package.json and README
|
|
341
|
+
const sourceFiles = Array.from(collectedFiles).filter((f) => !f.endsWith('package.json') && !f.toLowerCase().includes('readme'));
|
|
342
|
+
if (sourceFiles.length === 0 &&
|
|
343
|
+
entryPoints.length === 0 &&
|
|
344
|
+
(!pkg.files || pkg.files.length === 0)) {
|
|
345
|
+
return {
|
|
346
|
+
files: [],
|
|
347
|
+
error: 'No files could be determined for example. Add main, module, exports, or files field to package.json.',
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
return { files: Array.from(collectedFiles) };
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Extract metadata from a package.json file per the design doc.
|
|
354
|
+
*/
|
|
355
|
+
function extractPackageMetadata(pkg) {
|
|
356
|
+
// name field is required
|
|
357
|
+
if (!pkg.name || typeof pkg.name !== 'string') {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
const functionalExamples = pkg['functional-examples'] ?? {};
|
|
361
|
+
const { title: feTitle, ...restFunctionalExamples } = functionalExamples;
|
|
362
|
+
// Strip scope from name for id
|
|
363
|
+
const id = stripScope(pkg.name);
|
|
364
|
+
// Title: functional-examples.title overrides, fallback to name
|
|
365
|
+
const title = typeof feTitle === 'string' ? feTitle : id;
|
|
366
|
+
// Description: direct from package.json
|
|
367
|
+
const description = typeof pkg.description === 'string' ? pkg.description : undefined;
|
|
368
|
+
// Tags: from keywords array
|
|
369
|
+
const tags = Array.isArray(pkg.keywords)
|
|
370
|
+
? pkg.keywords.filter((k) => typeof k === 'string')
|
|
371
|
+
: undefined;
|
|
372
|
+
// Metadata: spread remaining functional-examples fields
|
|
373
|
+
const metadata = { ...restFunctionalExamples };
|
|
374
|
+
return {
|
|
375
|
+
id,
|
|
376
|
+
title,
|
|
377
|
+
description,
|
|
378
|
+
tags,
|
|
379
|
+
metadata,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Create a JavaScript/TypeScript extractor.
|
|
384
|
+
*
|
|
385
|
+
* This extractor:
|
|
386
|
+
* - Receives pre-filtered candidates (files and directories)
|
|
387
|
+
* - For directories with package.json: treats as multi-file package example
|
|
388
|
+
* - For files with frontmatter: treats as single-file example
|
|
389
|
+
* - Extracts metadata from package.json or frontmatter
|
|
390
|
+
* - Loads raw content into ExampleFile
|
|
391
|
+
* - Respects exclude patterns (default: node_modules, .git, dist, build)
|
|
392
|
+
* - Tracks claimed files for conflict detection
|
|
393
|
+
*/
|
|
394
|
+
export function createJavaScriptExtractor() {
|
|
395
|
+
async function tryExtractFromFile(absolutePath, rootPath) {
|
|
396
|
+
let content;
|
|
397
|
+
try {
|
|
398
|
+
content = await fs.readFile(absolutePath, 'utf-8');
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
const metadata = await extractFrontmatter(content);
|
|
404
|
+
if (!metadata || !hasValidMetadata(metadata)) {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
const { id, title, description, ...restMetadata } = metadata;
|
|
408
|
+
const relativePath = path.relative(rootPath, absolutePath);
|
|
409
|
+
return {
|
|
410
|
+
id,
|
|
411
|
+
title,
|
|
412
|
+
description: typeof description === 'string' ? description : undefined,
|
|
413
|
+
rootPath: absolutePath,
|
|
414
|
+
files: [{ absolutePath, relativePath, raw: content }],
|
|
415
|
+
metadata: restMetadata,
|
|
416
|
+
extractorName: EXTRACTOR_NAME,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
async function tryExtractFromPackageJson(packageJsonPath, options) {
|
|
420
|
+
const exampleDir = path.dirname(packageJsonPath);
|
|
421
|
+
const readResult = await readPackageJson(packageJsonPath);
|
|
422
|
+
if (!readResult) {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
if ('error' in readResult) {
|
|
426
|
+
return {
|
|
427
|
+
error: {
|
|
428
|
+
path: packageJsonPath,
|
|
429
|
+
message: readResult.error,
|
|
430
|
+
},
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
const pkg = readResult.pkg;
|
|
434
|
+
const extracted = extractPackageMetadata(pkg);
|
|
435
|
+
if (!extracted) {
|
|
436
|
+
// No valid name field - not a valid package example
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
const { id, title, description, tags, metadata } = extracted;
|
|
440
|
+
// Collect files for this package example
|
|
441
|
+
const { files: filePaths, error: collectError } = await collectPackageFiles(exampleDir, pkg, options);
|
|
442
|
+
if (collectError) {
|
|
443
|
+
return {
|
|
444
|
+
error: {
|
|
445
|
+
path: packageJsonPath,
|
|
446
|
+
message: collectError,
|
|
447
|
+
},
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
// Build ExampleFile array
|
|
451
|
+
const exampleFiles = [];
|
|
452
|
+
for (const filePath of filePaths) {
|
|
453
|
+
try {
|
|
454
|
+
let raw = await fs.readFile(filePath, 'utf-8');
|
|
455
|
+
// Strip functional-examples key from package.json
|
|
456
|
+
if (filePath.endsWith('package.json')) {
|
|
457
|
+
raw = stripFunctionalExamplesKey(raw);
|
|
458
|
+
}
|
|
459
|
+
const relativePath = path.relative(exampleDir, filePath);
|
|
460
|
+
exampleFiles.push({
|
|
461
|
+
absolutePath: filePath,
|
|
462
|
+
relativePath,
|
|
463
|
+
raw,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
// Skip files that can't be read
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
// Include tags in metadata if present
|
|
471
|
+
const finalMetadata = { ...metadata };
|
|
472
|
+
if (tags && tags.length > 0) {
|
|
473
|
+
finalMetadata.tags = tags;
|
|
474
|
+
}
|
|
475
|
+
const example = {
|
|
476
|
+
id,
|
|
477
|
+
title,
|
|
478
|
+
description,
|
|
479
|
+
rootPath: exampleDir,
|
|
480
|
+
files: exampleFiles,
|
|
481
|
+
metadata: finalMetadata,
|
|
482
|
+
extractorName: EXTRACTOR_NAME,
|
|
483
|
+
};
|
|
484
|
+
return { example, files: filePaths };
|
|
485
|
+
}
|
|
486
|
+
return {
|
|
487
|
+
name: EXTRACTOR_NAME,
|
|
488
|
+
async extract(candidates, options) {
|
|
489
|
+
const examples = [];
|
|
490
|
+
const claimedFiles = new Set();
|
|
491
|
+
const errors = [];
|
|
492
|
+
for (const candidate of candidates) {
|
|
493
|
+
if (options.signal?.aborted)
|
|
494
|
+
break;
|
|
495
|
+
const fullPath = path.join(candidate.parentPath, candidate.name);
|
|
496
|
+
// Handle file candidates
|
|
497
|
+
if (candidate.isFile()) {
|
|
498
|
+
// Check if it's a package.json file - resolve to parent directory
|
|
499
|
+
if (candidate.name === 'package.json') {
|
|
500
|
+
const result = await tryExtractFromPackageJson(fullPath, options);
|
|
501
|
+
if (result) {
|
|
502
|
+
if ('error' in result) {
|
|
503
|
+
errors.push(result.error);
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
examples.push(result.example);
|
|
507
|
+
for (const file of result.files) {
|
|
508
|
+
claimedFiles.add(file);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
// Handle regular JS/TS files with frontmatter
|
|
515
|
+
const ext = path.extname(candidate.name);
|
|
516
|
+
if (!FILE_EXTENSIONS.includes(ext))
|
|
517
|
+
continue;
|
|
518
|
+
const example = await tryExtractFromFile(fullPath, options.rootPath);
|
|
519
|
+
if (example) {
|
|
520
|
+
examples.push(example);
|
|
521
|
+
claimedFiles.add(fullPath);
|
|
522
|
+
}
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
// Handle directory candidates
|
|
526
|
+
if (candidate.isDirectory()) {
|
|
527
|
+
// Skip excluded directories (default ones)
|
|
528
|
+
if (DEFAULT_EXCLUDED_DIRS.has(candidate.name)) {
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
// Skip directories matching custom exclude patterns
|
|
532
|
+
const excludePatterns = options.exclude ?? [];
|
|
533
|
+
const relativeDirPath = path.relative(options.rootPath, fullPath);
|
|
534
|
+
const pathsToCheck = [
|
|
535
|
+
candidate.name,
|
|
536
|
+
relativeDirPath,
|
|
537
|
+
`${relativeDirPath}/`,
|
|
538
|
+
`${candidate.name}/`,
|
|
539
|
+
];
|
|
540
|
+
const matchers = await Promise.all(excludePatterns.map((pattern) => createMatcher(pattern)));
|
|
541
|
+
const isExcluded = matchers.some((matcher) => {
|
|
542
|
+
return pathsToCheck.some((p) => matcher(p));
|
|
543
|
+
});
|
|
544
|
+
if (isExcluded) {
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
// Check for package.json in the directory
|
|
548
|
+
const packageJsonPath = path.join(fullPath, 'package.json');
|
|
549
|
+
if (await fileExists(packageJsonPath)) {
|
|
550
|
+
const result = await tryExtractFromPackageJson(packageJsonPath, options);
|
|
551
|
+
if (result) {
|
|
552
|
+
if ('error' in result) {
|
|
553
|
+
errors.push(result.error);
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
examples.push(result.example);
|
|
557
|
+
for (const file of result.files) {
|
|
558
|
+
claimedFiles.add(file);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
// If package.json was found, don't also scan for single-file examples
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
// No package.json - fall back to scanning for single-file examples with frontmatter
|
|
566
|
+
const fileExcludePatterns = [
|
|
567
|
+
...DEFAULT_EXCLUDE_PATTERNS,
|
|
568
|
+
...(options.exclude ?? []),
|
|
569
|
+
];
|
|
570
|
+
const files = await glob(FILE_PATTERNS, {
|
|
571
|
+
cwd: fullPath,
|
|
572
|
+
absolute: true,
|
|
573
|
+
ignore: fileExcludePatterns,
|
|
574
|
+
onlyFiles: true,
|
|
575
|
+
});
|
|
576
|
+
for (const filePath of files) {
|
|
577
|
+
const example = await tryExtractFromFile(filePath, options.rootPath);
|
|
578
|
+
if (example) {
|
|
579
|
+
examples.push(example);
|
|
580
|
+
claimedFiles.add(filePath);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return { examples, errors, claimedFiles };
|
|
586
|
+
},
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
//# sourceMappingURL=extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractor.js","sourceRoot":"","sources":["../src/extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type FileContentsParser } from '@functional-examples/devkit';
|
|
2
|
+
/**
|
|
3
|
+
* Create a FileContentsParser that extracts YAML frontmatter from JavaScript/TypeScript files.
|
|
4
|
+
*
|
|
5
|
+
* Supports two formats:
|
|
6
|
+
* 1. Line comment style:
|
|
7
|
+
* // ---
|
|
8
|
+
* // title: Example
|
|
9
|
+
* // ---
|
|
10
|
+
*
|
|
11
|
+
* 2. Block comment wrapped style:
|
|
12
|
+
* /* ---
|
|
13
|
+
* title: Example
|
|
14
|
+
* --- *\/
|
|
15
|
+
*
|
|
16
|
+
* Frontmatter must be at the very start of the file (line 1).
|
|
17
|
+
* Extracted metadata is merged into context.metadata.
|
|
18
|
+
* The frontmatter block is stripped from context.parsed.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createFrontmatterParser(): FileContentsParser;
|
|
21
|
+
//# sourceMappingURL=frontmatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../src/frontmatter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EAGxB,MAAM,6BAA6B,CAAC;AA+HrC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,IAAI,kBAAkB,CAkC5D"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { parseYaml, } from '@functional-examples/devkit';
|
|
2
|
+
/** Pattern matching line comment frontmatter start: // --- */
|
|
3
|
+
const LINE_COMMENT_START = /^[ \t]*\/\/\s*---\s*$/;
|
|
4
|
+
/** Pattern matching line comment frontmatter end: // --- */
|
|
5
|
+
const LINE_COMMENT_END = /^[ \t]*\/\/\s*---\s*$/;
|
|
6
|
+
/** Pattern matching line comment content: // <content> */
|
|
7
|
+
const LINE_COMMENT_CONTENT = /^[ \t]*\/\/\s?(.*)$/;
|
|
8
|
+
/** Pattern matching block comment frontmatter start: /* --- */
|
|
9
|
+
const BLOCK_COMMENT_START = /^[ \t]*\/\*\s*---\s*$/;
|
|
10
|
+
/** Pattern matching block comment frontmatter end: --- */
|
|
11
|
+
const BLOCK_COMMENT_END = /^[ \t]*---\s*\*\/\s*$/;
|
|
12
|
+
/**
|
|
13
|
+
* Try to extract line comment style frontmatter from the start of the file.
|
|
14
|
+
* Format:
|
|
15
|
+
* // ---
|
|
16
|
+
* // key: value
|
|
17
|
+
* // ---
|
|
18
|
+
*/
|
|
19
|
+
async function extractLineCommentFrontmatter(lines) {
|
|
20
|
+
if (lines.length < 2)
|
|
21
|
+
return null;
|
|
22
|
+
// First line must be // ---
|
|
23
|
+
if (!LINE_COMMENT_START.test(lines[0])) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
// Find closing delimiter
|
|
27
|
+
const yamlLines = [];
|
|
28
|
+
let endIndex = -1;
|
|
29
|
+
for (let i = 1; i < lines.length; i++) {
|
|
30
|
+
const line = lines[i];
|
|
31
|
+
// Check for closing delimiter
|
|
32
|
+
if (LINE_COMMENT_END.test(line)) {
|
|
33
|
+
endIndex = i;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
// Extract content from line comment
|
|
37
|
+
const match = line.match(LINE_COMMENT_CONTENT);
|
|
38
|
+
if (match) {
|
|
39
|
+
yamlLines.push(match[1]);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Non-comment line before closing delimiter - invalid frontmatter
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (endIndex === -1) {
|
|
47
|
+
// No closing delimiter found
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const yamlContent = yamlLines.join('\n');
|
|
51
|
+
const metadata = yamlContent.trim()
|
|
52
|
+
? (await parseYaml(yamlContent)) ?? {}
|
|
53
|
+
: {};
|
|
54
|
+
return {
|
|
55
|
+
metadata,
|
|
56
|
+
linesConsumed: endIndex + 1,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Try to extract block comment style frontmatter from the start of the file.
|
|
61
|
+
* Format:
|
|
62
|
+
* /* ---
|
|
63
|
+
* key: value
|
|
64
|
+
* --- *\/
|
|
65
|
+
*/
|
|
66
|
+
async function extractBlockCommentFrontmatter(lines) {
|
|
67
|
+
if (lines.length < 2)
|
|
68
|
+
return null;
|
|
69
|
+
// First line must be /* ---
|
|
70
|
+
if (!BLOCK_COMMENT_START.test(lines[0])) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
// Find closing delimiter
|
|
74
|
+
const yamlLines = [];
|
|
75
|
+
let endIndex = -1;
|
|
76
|
+
for (let i = 1; i < lines.length; i++) {
|
|
77
|
+
const line = lines[i];
|
|
78
|
+
// Check for closing delimiter
|
|
79
|
+
if (BLOCK_COMMENT_END.test(line)) {
|
|
80
|
+
endIndex = i;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
// Content lines are raw YAML (no prefix stripping needed)
|
|
84
|
+
yamlLines.push(line);
|
|
85
|
+
}
|
|
86
|
+
if (endIndex === -1) {
|
|
87
|
+
// No closing delimiter found
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const yamlContent = yamlLines.join('\n');
|
|
91
|
+
const metadata = yamlContent.trim()
|
|
92
|
+
? (await parseYaml(yamlContent)) ?? {}
|
|
93
|
+
: {};
|
|
94
|
+
return {
|
|
95
|
+
metadata,
|
|
96
|
+
linesConsumed: endIndex + 1,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Create a FileContentsParser that extracts YAML frontmatter from JavaScript/TypeScript files.
|
|
101
|
+
*
|
|
102
|
+
* Supports two formats:
|
|
103
|
+
* 1. Line comment style:
|
|
104
|
+
* // ---
|
|
105
|
+
* // title: Example
|
|
106
|
+
* // ---
|
|
107
|
+
*
|
|
108
|
+
* 2. Block comment wrapped style:
|
|
109
|
+
* /* ---
|
|
110
|
+
* title: Example
|
|
111
|
+
* --- *\/
|
|
112
|
+
*
|
|
113
|
+
* Frontmatter must be at the very start of the file (line 1).
|
|
114
|
+
* Extracted metadata is merged into context.metadata.
|
|
115
|
+
* The frontmatter block is stripped from context.parsed.
|
|
116
|
+
*/
|
|
117
|
+
export function createFrontmatterParser() {
|
|
118
|
+
return {
|
|
119
|
+
name: 'javascript-frontmatter-parser',
|
|
120
|
+
async parse(context) {
|
|
121
|
+
const lines = context.parsed.split('\n');
|
|
122
|
+
// Try line comment style first, then block comment style
|
|
123
|
+
const result = (await extractLineCommentFrontmatter(lines)) ??
|
|
124
|
+
(await extractBlockCommentFrontmatter(lines));
|
|
125
|
+
if (!result) {
|
|
126
|
+
// No frontmatter found, return context unchanged
|
|
127
|
+
return context;
|
|
128
|
+
}
|
|
129
|
+
// Strip frontmatter lines from parsed content
|
|
130
|
+
const remainingLines = lines.slice(result.linesConsumed);
|
|
131
|
+
const parsed = remainingLines.join('\n');
|
|
132
|
+
// Merge extracted metadata with existing metadata
|
|
133
|
+
const metadata = {
|
|
134
|
+
...context.metadata,
|
|
135
|
+
...result.metadata,
|
|
136
|
+
};
|
|
137
|
+
return {
|
|
138
|
+
...context,
|
|
139
|
+
parsed,
|
|
140
|
+
metadata,
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../src/frontmatter.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Plugin } from '@functional-examples/devkit';
|
|
2
|
+
export declare const JAVASCRIPT_EXTENSIONS: readonly [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts"];
|
|
3
|
+
export { createJavaScriptParser } from './parser.js';
|
|
4
|
+
export { createFrontmatterParser } from './frontmatter.js';
|
|
5
|
+
export { createJavaScriptExtractor } from './extractor.js';
|
|
6
|
+
/**
|
|
7
|
+
* Options for the JavaScript plugin.
|
|
8
|
+
*/
|
|
9
|
+
export interface JavaScriptPluginOptions {
|
|
10
|
+
/** Skip frontmatter extraction (default: false) */
|
|
11
|
+
skipFrontmatter?: boolean;
|
|
12
|
+
/** Skip region extraction (default: false) */
|
|
13
|
+
skipRegions?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a JavaScript/TypeScript plugin for functional-examples.
|
|
17
|
+
*
|
|
18
|
+
* This plugin:
|
|
19
|
+
* - Handles .js, .jsx, .mjs, .cjs, .ts, .tsx, .mts, .cts files
|
|
20
|
+
* - Extracts YAML frontmatter from line/block comments
|
|
21
|
+
* - Extracts code regions from #region/#endregion markers
|
|
22
|
+
* - Provides a single-file extractor for discovering examples
|
|
23
|
+
*
|
|
24
|
+
* The combined parser runs frontmatter parsing FIRST, then region parsing.
|
|
25
|
+
* This ensures frontmatter is stripped before region markers are processed.
|
|
26
|
+
*
|
|
27
|
+
* @param options - Optional plugin configuration
|
|
28
|
+
* @returns A configured JavaScript plugin
|
|
29
|
+
*/
|
|
30
|
+
export declare function createJavaScriptPlugin(options?: JavaScriptPluginOptions): Plugin;
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EAIP,MAAM,6BAA6B,CAAC;AAKrC,eAAO,MAAM,qBAAqB,yEASxB,CAAC;AAEX,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,mDAAmD;IACnD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,8CAA8C;IAC9C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA6DD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CAsCR"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { createJavaScriptParser } from './parser.js';
|
|
2
|
+
import { createFrontmatterParser } from './frontmatter.js';
|
|
3
|
+
import { createJavaScriptExtractor } from './extractor.js';
|
|
4
|
+
export const JAVASCRIPT_EXTENSIONS = [
|
|
5
|
+
'.js',
|
|
6
|
+
'.jsx',
|
|
7
|
+
'.mjs',
|
|
8
|
+
'.cjs',
|
|
9
|
+
'.ts',
|
|
10
|
+
'.tsx',
|
|
11
|
+
'.mts',
|
|
12
|
+
'.cts',
|
|
13
|
+
];
|
|
14
|
+
export { createJavaScriptParser } from './parser.js';
|
|
15
|
+
export { createFrontmatterParser } from './frontmatter.js';
|
|
16
|
+
export { createJavaScriptExtractor } from './extractor.js';
|
|
17
|
+
/**
|
|
18
|
+
* JSON Schema for JavaScript plugin options.
|
|
19
|
+
*/
|
|
20
|
+
const OPTIONS_SCHEMA = JSON.stringify({
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
skipFrontmatter: {
|
|
24
|
+
type: 'boolean',
|
|
25
|
+
description: 'Skip frontmatter parsing',
|
|
26
|
+
},
|
|
27
|
+
skipRegions: {
|
|
28
|
+
type: 'boolean',
|
|
29
|
+
description: 'Skip region parsing',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* JSON Schema for metadata this plugin expects.
|
|
35
|
+
*/
|
|
36
|
+
const METADATA_SCHEMA = JSON.stringify({
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
id: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'Unique example identifier',
|
|
42
|
+
},
|
|
43
|
+
title: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'Example title',
|
|
46
|
+
},
|
|
47
|
+
description: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: 'Example description',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
required: ['id', 'title'],
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Validate metadata extracted by this plugin.
|
|
56
|
+
* Note: id and title are validated by the extractor and are top-level example fields,
|
|
57
|
+
* not part of the metadata object passed here.
|
|
58
|
+
*/
|
|
59
|
+
function validateMetadata(metadata) {
|
|
60
|
+
const errors = [];
|
|
61
|
+
// Validate tags array if present
|
|
62
|
+
if (metadata.tags !== undefined) {
|
|
63
|
+
if (!Array.isArray(metadata.tags)) {
|
|
64
|
+
errors.push({ path: 'tags', message: 'must be an array' });
|
|
65
|
+
}
|
|
66
|
+
else if (!metadata.tags.every((t) => typeof t === 'string')) {
|
|
67
|
+
errors.push({ path: 'tags', message: 'must be an array of strings' });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { success: errors.length === 0, errors };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Create a JavaScript/TypeScript plugin for functional-examples.
|
|
74
|
+
*
|
|
75
|
+
* This plugin:
|
|
76
|
+
* - Handles .js, .jsx, .mjs, .cjs, .ts, .tsx, .mts, .cts files
|
|
77
|
+
* - Extracts YAML frontmatter from line/block comments
|
|
78
|
+
* - Extracts code regions from #region/#endregion markers
|
|
79
|
+
* - Provides a single-file extractor for discovering examples
|
|
80
|
+
*
|
|
81
|
+
* The combined parser runs frontmatter parsing FIRST, then region parsing.
|
|
82
|
+
* This ensures frontmatter is stripped before region markers are processed.
|
|
83
|
+
*
|
|
84
|
+
* @param options - Optional plugin configuration
|
|
85
|
+
* @returns A configured JavaScript plugin
|
|
86
|
+
*/
|
|
87
|
+
export function createJavaScriptPlugin(options) {
|
|
88
|
+
const { skipFrontmatter = false, skipRegions = false } = options ?? {};
|
|
89
|
+
// Create a combined parser that chains frontmatter → regions
|
|
90
|
+
const combinedParser = {
|
|
91
|
+
name: 'javascript-combined-parser',
|
|
92
|
+
async parse(context) {
|
|
93
|
+
let result = context;
|
|
94
|
+
// Run frontmatter parser first (extracts metadata, strips frontmatter)
|
|
95
|
+
if (!skipFrontmatter) {
|
|
96
|
+
result = await createFrontmatterParser().parse(result);
|
|
97
|
+
}
|
|
98
|
+
// Run region parser second (extracts hunks, strips markers)
|
|
99
|
+
if (!skipRegions) {
|
|
100
|
+
result = createJavaScriptParser().parse(result);
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
return {
|
|
106
|
+
name: 'javascript',
|
|
107
|
+
extensions: [...JAVASCRIPT_EXTENSIONS],
|
|
108
|
+
extractor: createJavaScriptExtractor(),
|
|
109
|
+
fileContentsParser: combinedParser,
|
|
110
|
+
schemas: {
|
|
111
|
+
options: OPTIONS_SCHEMA,
|
|
112
|
+
metadata: METADATA_SCHEMA,
|
|
113
|
+
},
|
|
114
|
+
validators: {
|
|
115
|
+
metadata: validateMetadata,
|
|
116
|
+
},
|
|
117
|
+
_options: options,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/parser.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FileContentsParser } from '@functional-examples/devkit';
|
|
2
|
+
/**
|
|
3
|
+
* Create a FileContentsParser for JavaScript/TypeScript files.
|
|
4
|
+
* Handles region extraction and marker stripping.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createJavaScriptParser(): FileContentsParser;
|
|
7
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAGnB,MAAM,6BAA6B,CAAC;AAcrC;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,kBAAkB,CA2D3D"}
|
package/dist/parser.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const LINE_COMMENT_REGION = /^[ \t]*\/\/\s*#region\s+(\S+)\s*$/;
|
|
2
|
+
const LINE_COMMENT_ENDREGION = /^[ \t]*\/\/\s*#endregion(?:\s+(\S+))?\s*$/;
|
|
3
|
+
const BLOCK_COMMENT_REGION = /^[ \t]*\/\*\s*#region\s+(\S+)\s*\*\/\s*$/;
|
|
4
|
+
const BLOCK_COMMENT_ENDREGION = /^[ \t]*\/\*\s*#endregion(?:\s+(\S+))?\s*\*\/\s*$/;
|
|
5
|
+
/**
|
|
6
|
+
* Create a FileContentsParser for JavaScript/TypeScript files.
|
|
7
|
+
* Handles region extraction and marker stripping.
|
|
8
|
+
*/
|
|
9
|
+
export function createJavaScriptParser() {
|
|
10
|
+
return {
|
|
11
|
+
name: 'javascript-parser',
|
|
12
|
+
parse(context) {
|
|
13
|
+
const lines = context.parsed.split('\n');
|
|
14
|
+
const hunks = [];
|
|
15
|
+
const outputLines = [];
|
|
16
|
+
const regionStack = [];
|
|
17
|
+
for (let i = 0; i < lines.length; i++) {
|
|
18
|
+
const line = lines[i];
|
|
19
|
+
const lineNum = i + 1;
|
|
20
|
+
// Check for region start
|
|
21
|
+
const startMatch = line.match(LINE_COMMENT_REGION) || line.match(BLOCK_COMMENT_REGION);
|
|
22
|
+
if (startMatch) {
|
|
23
|
+
regionStack.push({
|
|
24
|
+
id: startMatch[1],
|
|
25
|
+
startLine: lineNum,
|
|
26
|
+
lines: [],
|
|
27
|
+
});
|
|
28
|
+
continue; // Don't include marker in output
|
|
29
|
+
}
|
|
30
|
+
// Check for region end
|
|
31
|
+
const endMatch = line.match(LINE_COMMENT_ENDREGION) ||
|
|
32
|
+
line.match(BLOCK_COMMENT_ENDREGION);
|
|
33
|
+
if (endMatch) {
|
|
34
|
+
const current = regionStack.pop();
|
|
35
|
+
if (current) {
|
|
36
|
+
hunks.push({
|
|
37
|
+
id: current.id,
|
|
38
|
+
content: current.lines.join('\n'),
|
|
39
|
+
startLine: current.startLine,
|
|
40
|
+
endLine: lineNum,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
continue; // Don't include marker in output
|
|
44
|
+
}
|
|
45
|
+
// Regular line - add to output and any active regions
|
|
46
|
+
outputLines.push(line);
|
|
47
|
+
for (const region of regionStack) {
|
|
48
|
+
region.lines.push(line);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
...context,
|
|
53
|
+
parsed: outputLines.join('\n'),
|
|
54
|
+
hunks,
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAMA,MAAM,mBAAmB,GAAG,mCAAmC,CAAC;AAChE,MAAM,sBAAsB,GAAG,2CAA2C,CAAC;AAC3E,MAAM,oBAAoB,GAAG,0CAA0C,CAAC;AACxE,MAAM,uBAAuB,GAC3B,kDAAkD,CAAC;AAQrD;;;GAGG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO;QACL,IAAI,EAAE,mBAAmB;QAEzB,KAAK,CAAC,OAAyB;YAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACzC,MAAM,KAAK,GAAmB,EAAE,CAAC;YACjC,MAAM,WAAW,GAAa,EAAE,CAAC;YACjC,MAAM,WAAW,GAAkB,EAAE,CAAC;YAEtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACtB,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;gBAEtB,yBAAyB;gBACzB,MAAM,UAAU,GACd,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;gBAEtE,IAAI,UAAU,EAAE,CAAC;oBACf,WAAW,CAAC,IAAI,CAAC;wBACf,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;wBACjB,SAAS,EAAE,OAAO;wBAClB,KAAK,EAAE,EAAE;qBACV,CAAC,CAAC;oBACH,SAAS,CAAC,iCAAiC;gBAC7C,CAAC;gBAED,uBAAuB;gBACvB,MAAM,QAAQ,GACZ,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC;oBAClC,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;gBAEtC,IAAI,QAAQ,EAAE,CAAC;oBACb,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;oBAClC,IAAI,OAAO,EAAE,CAAC;wBACZ,KAAK,CAAC,IAAI,CAAC;4BACT,EAAE,EAAE,OAAO,CAAC,EAAE;4BACd,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;4BACjC,SAAS,EAAE,OAAO,CAAC,SAAS;4BAC5B,OAAO,EAAE,OAAO;yBACjB,CAAC,CAAC;oBACL,CAAC;oBACD,SAAS,CAAC,iCAAiC;gBAC7C,CAAC;gBAED,sDAAsD;gBACtD,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACvB,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;oBACjC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;YAED,OAAO;gBACL,GAAG,OAAO;gBACV,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC9B,KAAK;aACN,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@functional-examples/javascript",
|
|
3
|
+
"version": "0.0.0-alpha.1",
|
|
4
|
+
"description": "JavaScript/TypeScript plugin for functional-examples",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"typescript": "^5.7.2",
|
|
23
|
+
"vitest": "^1.3.1",
|
|
24
|
+
"@functional-examples/devkit": "0.0.0-alpha.1"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"dependency-tree": "^11.0.0",
|
|
28
|
+
"picomatch": "^4.0.3",
|
|
29
|
+
"tinyglobby": "^0.2.15",
|
|
30
|
+
"yaml": "^2.7.1",
|
|
31
|
+
"@functional-examples/devkit": "0.0.0-alpha.1"
|
|
32
|
+
},
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"author": {
|
|
35
|
+
"name": "Craigory Coppola",
|
|
36
|
+
"url": "https://craigory.dev"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://craigory.dev/functional-examples",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/AgentEnder/functional-examples.git"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/AgentEnder/functional-examples/issues"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc -p tsconfig.lib.json",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:watch": "vitest"
|
|
50
|
+
}
|
|
51
|
+
}
|