drupal-canvas-react 0.1.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/dist/cli.js ADDED
@@ -0,0 +1,457 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import * as fs3 from "fs";
5
+ import * as path4 from "path";
6
+
7
+ // src/config.ts
8
+ import * as fs from "fs";
9
+ import * as path from "path";
10
+ import { pathToFileURL } from "url";
11
+ function validateConfig(config, source) {
12
+ if (!config || typeof config !== "object") {
13
+ throw new Error(`Invalid config from ${source}: must be an object`);
14
+ }
15
+ const cfg = config;
16
+ if (typeof cfg.outDir !== "string") {
17
+ throw new Error(`Invalid config from ${source}: outDir is required and must be a string`);
18
+ }
19
+ if (!cfg.components || typeof cfg.components !== "object") {
20
+ throw new Error(`Invalid config from ${source}: components is required and must be an object`);
21
+ }
22
+ return config;
23
+ }
24
+ async function loadConfig(cwd = process.cwd()) {
25
+ const configFiles = [
26
+ "canvas.config.mts",
27
+ // ESM TypeScript (no warning)
28
+ "canvas.config.ts",
29
+ "canvas.config.mjs",
30
+ // ESM JavaScript
31
+ "canvas.config.js"
32
+ ];
33
+ for (const filename of configFiles) {
34
+ const configPath = path.join(cwd, filename);
35
+ if (fs.existsSync(configPath)) {
36
+ const module = await import(pathToFileURL(configPath).href);
37
+ const config = module.default || module;
38
+ return validateConfig(config, configPath);
39
+ }
40
+ }
41
+ const packageJsonPath = path.join(cwd, "package.json");
42
+ if (fs.existsSync(packageJsonPath)) {
43
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
44
+ if (packageJson["drupal-canvas"]) {
45
+ return validateConfig(packageJson["drupal-canvas"], "package.json#drupal-canvas");
46
+ }
47
+ }
48
+ throw new Error(
49
+ `No config found. Create canvas.config.mts (recommended), canvas.config.ts, or add "drupal-canvas" to package.json`
50
+ );
51
+ }
52
+
53
+ // src/generate-index.ts
54
+ import { parse } from "react-docgen-typescript";
55
+ import * as fs2 from "fs";
56
+ import * as path2 from "path";
57
+ var CANVAS_IMAGE_REF = "json-schema-definitions://canvas.module/image";
58
+ function toOutputProperty(prop) {
59
+ if (prop.type === "image") {
60
+ return {
61
+ type: "object",
62
+ title: prop.title,
63
+ description: prop.description,
64
+ $ref: CANVAS_IMAGE_REF
65
+ };
66
+ }
67
+ return prop;
68
+ }
69
+ function convertTypeToJsonSchema(typeName) {
70
+ if (typeName === "number") return { type: "number" };
71
+ if (typeName === "boolean") return { type: "boolean" };
72
+ if (typeName === "string") return { type: "string" };
73
+ if (typeName === "enum" || /^["']/.test(typeName)) return { type: "string" };
74
+ if (typeName.includes("{") || typeName.includes("[")) {
75
+ return { incompatible: true };
76
+ }
77
+ if (/^[A-Z]/.test(typeName)) {
78
+ return { incompatible: true };
79
+ }
80
+ return { type: "string" };
81
+ }
82
+ function extractEnumValues(typeString) {
83
+ if (!typeString) return void 0;
84
+ const matches = typeString.match(/["']([^"']+)["']/g);
85
+ if (!matches || matches.length === 0) return void 0;
86
+ return matches.map((m) => m.slice(1, -1));
87
+ }
88
+ function extractEnumFromSource(sourceCode, propName) {
89
+ const pattern = new RegExp(
90
+ `${propName}\\??:\\s*(['"][^'"]+['"](?:\\s*\\|\\s*['"][^'"]+['"])*)`,
91
+ "m"
92
+ );
93
+ const match = sourceCode.match(pattern);
94
+ if (!match) return void 0;
95
+ return extractEnumValues(match[1]);
96
+ }
97
+ function extractPropOrder(sourceCode) {
98
+ const propPattern = /^\s*(\w+)\??:/gm;
99
+ const props = [];
100
+ let match;
101
+ while ((match = propPattern.exec(sourceCode)) !== null) {
102
+ if (!props.includes(match[1])) {
103
+ props.push(match[1]);
104
+ }
105
+ }
106
+ return props;
107
+ }
108
+ function sortPropertiesBySourceOrder(properties, sourceOrder) {
109
+ const sorted = {};
110
+ for (const propName of sourceOrder) {
111
+ if (properties[propName]) {
112
+ sorted[propName] = properties[propName];
113
+ }
114
+ }
115
+ for (const propName of Object.keys(properties)) {
116
+ if (!sorted[propName]) {
117
+ sorted[propName] = properties[propName];
118
+ }
119
+ }
120
+ return sorted;
121
+ }
122
+ function parseTitleAndDescription(comment, propName) {
123
+ const defaultTitle = propName.replace(/([A-Z])/g, " $1").toLowerCase().replace(/^./, (c) => c.toUpperCase()).trim();
124
+ if (!comment) {
125
+ return { title: defaultTitle, description: void 0 };
126
+ }
127
+ const match = comment.match(/^([^:]+):\s*(.*)$/);
128
+ if (match) {
129
+ return {
130
+ title: match[1].trim(),
131
+ description: match[2].trim() || void 0
132
+ };
133
+ }
134
+ return { title: defaultTitle, description: comment };
135
+ }
136
+ function isReactNodeType(typeName) {
137
+ return typeName === "ReactNode" || typeName.includes("ReactNode") || typeName === "React.ReactNode" || typeName.includes("React.ReactNode");
138
+ }
139
+ async function generateComponentIndex(config, options = {}) {
140
+ const cwd = options.cwd || process.cwd();
141
+ const defaultCategory = config.defaultCategory || "Components";
142
+ const idPrefix = config.idPrefix || "";
143
+ const components = config.components;
144
+ const result = [];
145
+ for (const [id, entry] of Object.entries(components)) {
146
+ const filePath = path2.resolve(cwd, entry.path);
147
+ const parsed = parse(filePath, {
148
+ shouldExtractLiteralValuesFromEnum: false,
149
+ shouldRemoveUndefinedFromOptional: true
150
+ });
151
+ const componentDoc = parsed.length > 0 ? parsed[0] : null;
152
+ const sourceCode = fs2.readFileSync(filePath, "utf-8");
153
+ const hasManualMeta = entry.name || entry.description || entry.props || entry.slots;
154
+ if (!componentDoc && !hasManualMeta) {
155
+ console.warn(`No component found in ${filePath} (add name/description to config to include)`);
156
+ continue;
157
+ }
158
+ const meta = {
159
+ name: entry.name || componentDoc?.displayName || id,
160
+ description: entry.description || componentDoc?.description || `${id} component`
161
+ };
162
+ const properties = {};
163
+ const slots = {};
164
+ for (const [propName, propInfo] of Object.entries(componentDoc?.props ?? {})) {
165
+ const parent = propInfo.parent;
166
+ if (parent && parent.fileName.includes("node_modules")) {
167
+ continue;
168
+ }
169
+ const { title, description } = parseTitleAndDescription(propInfo.description, propName);
170
+ if (isReactNodeType(propInfo.type.name)) {
171
+ slots[propName] = {
172
+ title,
173
+ description
174
+ };
175
+ continue;
176
+ }
177
+ const typeResult = convertTypeToJsonSchema(propInfo.type.name);
178
+ if (typeResult.incompatible) {
179
+ console.warn(
180
+ ` \u26A0 ${id}.${propName}: type "${propInfo.type.name}" is not supported`
181
+ );
182
+ continue;
183
+ }
184
+ const looksLikeEnum = /["'][^"']+["']\s*\|/.test(propInfo.type.name);
185
+ const enumValues = looksLikeEnum ? extractEnumFromSource(sourceCode, propName) : void 0;
186
+ properties[propName] = {
187
+ type: typeResult.type,
188
+ title,
189
+ description,
190
+ default: propInfo.defaultValue?.value,
191
+ enum: enumValues
192
+ };
193
+ }
194
+ const mergedProperties = { ...properties };
195
+ for (const [propName, propValue] of Object.entries(entry.props ?? {})) {
196
+ if (propValue === false) {
197
+ delete mergedProperties[propName];
198
+ } else {
199
+ mergedProperties[propName] = propValue;
200
+ }
201
+ delete slots[propName];
202
+ }
203
+ const mergedSlots = { ...slots, ...entry.slots };
204
+ const propOrder = extractPropOrder(sourceCode);
205
+ const sortedProperties = sortPropertiesBySourceOrder(mergedProperties, propOrder);
206
+ const outputProperties = {};
207
+ for (const [propName, prop] of Object.entries(sortedProperties)) {
208
+ outputProperties[propName] = toOutputProperty(prop);
209
+ }
210
+ result.push({
211
+ id: idPrefix + id,
212
+ name: meta.name,
213
+ category: entry.category || defaultCategory,
214
+ description: meta.description,
215
+ status: "stable",
216
+ props: {
217
+ type: "object",
218
+ properties: outputProperties
219
+ },
220
+ slots: Object.keys(mergedSlots).length > 0 ? mergedSlots : void 0
221
+ });
222
+ }
223
+ return {
224
+ version: "1.0",
225
+ components: result
226
+ };
227
+ }
228
+ async function writeComponentIndex(config, options = {}) {
229
+ const cwd = options.cwd || process.cwd();
230
+ const index = await generateComponentIndex(config, options);
231
+ const outDir = path2.resolve(cwd, config.outDir);
232
+ if (!fs2.existsSync(outDir)) {
233
+ fs2.mkdirSync(outDir, { recursive: true });
234
+ }
235
+ const outputPath = path2.join(outDir, "component-index.json");
236
+ fs2.writeFileSync(outputPath, JSON.stringify(index, null, 2) + "\n");
237
+ console.log(`Generated ${outputPath}`);
238
+ }
239
+
240
+ // src/tsup-config.ts
241
+ import { createRequire } from "module";
242
+ import * as path3 from "path";
243
+ var require2 = createRequire(import.meta.url);
244
+ function getBuiltinStubsDir() {
245
+ return path3.join(path3.dirname(new URL(import.meta.url).pathname), "stubs");
246
+ }
247
+ function createTsupConfig(config, options = {}) {
248
+ const cwd = options.cwd || process.cwd();
249
+ const builtinStubsDir = getBuiltinStubsDir();
250
+ const entryFile = options.entry;
251
+ if (!entryFile) {
252
+ throw new Error("Entry file is required");
253
+ }
254
+ const outDir = path3.resolve(cwd, config.outDir);
255
+ const outputFilename = options.outputFilename || "drupal-canvas.js";
256
+ const reactDir = path3.dirname(require2.resolve("react/package.json"));
257
+ const reactDomDir = path3.dirname(require2.resolve("react-dom/package.json"));
258
+ const alias = {
259
+ react: reactDir,
260
+ "react-dom": reactDomDir,
261
+ "next/image": path3.join(builtinStubsDir, "image.js"),
262
+ "next/link": path3.join(builtinStubsDir, "link.js")
263
+ };
264
+ if (config.stubs) {
265
+ for (const [moduleName, stubPath] of Object.entries(config.stubs)) {
266
+ alias[moduleName] = path3.resolve(cwd, stubPath);
267
+ }
268
+ }
269
+ const baseName = outputFilename.replace(/\.js$/, "");
270
+ return {
271
+ entry: { [baseName]: entryFile },
272
+ format: "esm",
273
+ platform: "browser",
274
+ target: "es2020",
275
+ outDir,
276
+ outExtension: () => ({ js: ".js" }),
277
+ noExternal: [/.*/],
278
+ minify: config.minify !== false,
279
+ splitting: false,
280
+ tsconfig: config.tsconfig ? path3.resolve(cwd, config.tsconfig) : void 0,
281
+ esbuildPlugins: config.esbuildPlugins,
282
+ esbuildOptions(esbuildOptions) {
283
+ esbuildOptions.alias = alias;
284
+ }
285
+ };
286
+ }
287
+
288
+ // src/cli.ts
289
+ var HELP = `
290
+ drupal-canvas-react - Build React components for Drupal Canvas integration
291
+
292
+ Usage:
293
+ drupal-canvas-react <command> [options]
294
+
295
+ Commands:
296
+ build Full build (index + bundle)
297
+ generate-index Generate only component-index.json
298
+ bundle Build only standalone.js
299
+ init Create canvas.config.ts template
300
+
301
+ Options:
302
+ --help, -h Show this help message
303
+ --version, -v Show version number
304
+
305
+ Examples:
306
+ npx drupal-canvas-react build
307
+ npx drupal-canvas-react generate-index
308
+ npx drupal-canvas-react init
309
+ `;
310
+ var CONFIG_TEMPLATE = `import { defineConfig } from 'drupal-canvas-react'
311
+
312
+ export default defineConfig({
313
+ // Output directory for generated files
314
+ outDir: '../back-end/web/components',
315
+
316
+ // Default category name for components in the Canvas UI
317
+ defaultCategory: 'My components',
318
+
319
+ // Optional: Output filename (default: 'drupal-canvas.js')
320
+ // outputFilename: 'drupal-canvas.js',
321
+
322
+ // Optional: Path to tsconfig for compilation
323
+ // tsconfig: './tsconfig.json',
324
+
325
+ // Optional: Custom module stubs
326
+ // stubs: {
327
+ // 'next/image': './my-stubs/image.tsx',
328
+ // },
329
+
330
+ // Component definitions
331
+ components: {
332
+ // Example component:
333
+ // TextBlock: {
334
+ // path: 'components/organisms/TextBlock/TextBlock.tsx',
335
+ // loader: () => import('./components/organisms/TextBlock/TextBlock'),
336
+ // },
337
+ },
338
+ })
339
+ `;
340
+ async function main() {
341
+ const args = process.argv.slice(2);
342
+ const command = args[0];
343
+ if (!command || command === "--help" || command === "-h") {
344
+ console.log(HELP);
345
+ process.exit(0);
346
+ }
347
+ if (command === "--version" || command === "-v") {
348
+ const packageJsonPath = path4.join(path4.dirname(new URL(import.meta.url).pathname), "..", "package.json");
349
+ const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
350
+ console.log(packageJson.version);
351
+ process.exit(0);
352
+ }
353
+ const cwd = process.cwd();
354
+ switch (command) {
355
+ case "init":
356
+ await init(cwd);
357
+ break;
358
+ case "generate-index":
359
+ await generateIndex(cwd);
360
+ break;
361
+ case "bundle":
362
+ await bundle(cwd);
363
+ break;
364
+ case "build":
365
+ await build(cwd);
366
+ break;
367
+ default:
368
+ console.error(`Unknown command: ${command}`);
369
+ console.log(HELP);
370
+ process.exit(1);
371
+ }
372
+ }
373
+ async function init(cwd) {
374
+ const configPath = path4.join(cwd, "canvas.config.ts");
375
+ if (fs3.existsSync(configPath)) {
376
+ console.error(`canvas.config.ts already exists at ${configPath}`);
377
+ process.exit(1);
378
+ }
379
+ fs3.writeFileSync(configPath, CONFIG_TEMPLATE);
380
+ console.log(`Created ${configPath}`);
381
+ console.log("\nNext steps:");
382
+ console.log("1. Edit canvas.config.ts to add your components");
383
+ console.log("2. Run `npx drupal-canvas-react build` to generate the component index and bundle");
384
+ }
385
+ async function generateIndex(cwd) {
386
+ const config = await loadConfig(cwd);
387
+ await writeComponentIndex(config, { cwd });
388
+ }
389
+ function generateEntryCode(config) {
390
+ const idPrefix = config.idPrefix || "";
391
+ const loaderImports = [];
392
+ const componentEntries = [];
393
+ for (const [id, entry] of Object.entries(config.components)) {
394
+ const loaderStr = entry.loader.toString();
395
+ const importMatch = loaderStr.match(/import\s*\(\s*['"]([^'"]+)['"]\s*\)/);
396
+ if (importMatch) {
397
+ const importPath = importMatch[1];
398
+ const prefixedId = idPrefix + id;
399
+ loaderImports.push(` '${prefixedId}': () => import('${importPath}'),`);
400
+ const entryParts = [
401
+ ` path: '${entry.path}'`,
402
+ ` loader: loaders['${prefixedId}']`
403
+ ];
404
+ if (entry.transformProps) {
405
+ entryParts.push(` transformProps: ${entry.transformProps.toString()}`);
406
+ }
407
+ componentEntries.push(` '${prefixedId}': {
408
+ ${entryParts.join(",\n")},
409
+ },`);
410
+ }
411
+ }
412
+ return `import { createRenderFunction } from 'drupal-canvas-react/preview'
413
+
414
+ const loaders = {
415
+ ${loaderImports.join("\n")}
416
+ }
417
+
418
+ const components = {
419
+ ${componentEntries.join("\n")}
420
+ }
421
+
422
+ export const render = createRenderFunction(components)
423
+
424
+ if (typeof window !== 'undefined') {
425
+ ;(window as any).render = render
426
+ }
427
+ `;
428
+ }
429
+ async function bundle(cwd) {
430
+ const config = await loadConfig(cwd);
431
+ const outputFilename = config.outputFilename || "drupal-canvas.js";
432
+ const entryContent = generateEntryCode(config);
433
+ const tempEntryPath = path4.join(cwd, ".drupal-canvas-entry.ts");
434
+ fs3.writeFileSync(tempEntryPath, entryContent);
435
+ try {
436
+ const tsupConfig = createTsupConfig(config, { cwd, entry: tempEntryPath, outputFilename });
437
+ const tsup = await import("tsup");
438
+ await tsup.build(tsupConfig);
439
+ console.log(`Built ${outputFilename} to ${config.outDir}`);
440
+ } finally {
441
+ if (fs3.existsSync(tempEntryPath)) {
442
+ fs3.unlinkSync(tempEntryPath);
443
+ }
444
+ }
445
+ }
446
+ async function build(cwd) {
447
+ console.log("Generating component index...");
448
+ await generateIndex(cwd);
449
+ console.log("\nBuilding standalone bundle...");
450
+ await bundle(cwd);
451
+ console.log("\nBuild complete!");
452
+ }
453
+ main().catch((err) => {
454
+ console.error(err);
455
+ process.exit(1);
456
+ });
457
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/generate-index.ts","../src/tsup-config.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { loadConfig } from './config.js'\nimport { writeComponentIndex } from './generate-index.js'\nimport { createTsupConfig } from './tsup-config.js'\nimport type { CanvasConfig } from './types.js'\n\nconst HELP = `\ndrupal-canvas-react - Build React components for Drupal Canvas integration\n\nUsage:\n drupal-canvas-react <command> [options]\n\nCommands:\n build Full build (index + bundle)\n generate-index Generate only component-index.json\n bundle Build only standalone.js\n init Create canvas.config.ts template\n\nOptions:\n --help, -h Show this help message\n --version, -v Show version number\n\nExamples:\n npx drupal-canvas-react build\n npx drupal-canvas-react generate-index\n npx drupal-canvas-react init\n`\n\nconst CONFIG_TEMPLATE = `import { defineConfig } from 'drupal-canvas-react'\n\nexport default defineConfig({\n // Output directory for generated files\n outDir: '../back-end/web/components',\n\n // Default category name for components in the Canvas UI\n defaultCategory: 'My components',\n\n // Optional: Output filename (default: 'drupal-canvas.js')\n // outputFilename: 'drupal-canvas.js',\n\n // Optional: Path to tsconfig for compilation\n // tsconfig: './tsconfig.json',\n\n // Optional: Custom module stubs\n // stubs: {\n // 'next/image': './my-stubs/image.tsx',\n // },\n\n // Component definitions\n components: {\n // Example component:\n // TextBlock: {\n // path: 'components/organisms/TextBlock/TextBlock.tsx',\n // loader: () => import('./components/organisms/TextBlock/TextBlock'),\n // },\n },\n})\n`\n\nasync function main() {\n const args = process.argv.slice(2)\n const command = args[0]\n\n if (!command || command === '--help' || command === '-h') {\n console.log(HELP)\n process.exit(0)\n }\n\n if (command === '--version' || command === '-v') {\n const packageJsonPath = path.join(path.dirname(new URL(import.meta.url).pathname), '..', 'package.json')\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n console.log(packageJson.version)\n process.exit(0)\n }\n\n const cwd = process.cwd()\n\n switch (command) {\n case 'init':\n await init(cwd)\n break\n\n case 'generate-index':\n await generateIndex(cwd)\n break\n\n case 'bundle':\n await bundle(cwd)\n break\n\n case 'build':\n await build(cwd)\n break\n\n default:\n console.error(`Unknown command: ${command}`)\n console.log(HELP)\n process.exit(1)\n }\n}\n\nasync function init(cwd: string) {\n const configPath = path.join(cwd, 'canvas.config.ts')\n\n if (fs.existsSync(configPath)) {\n console.error(`canvas.config.ts already exists at ${configPath}`)\n process.exit(1)\n }\n\n fs.writeFileSync(configPath, CONFIG_TEMPLATE)\n console.log(`Created ${configPath}`)\n console.log('\\nNext steps:')\n console.log('1. Edit canvas.config.ts to add your components')\n console.log('2. Run `npx drupal-canvas-react build` to generate the component index and bundle')\n}\n\nasync function generateIndex(cwd: string) {\n const config = await loadConfig(cwd)\n await writeComponentIndex(config, { cwd })\n}\n\nfunction generateEntryCode(config: CanvasConfig): string {\n const idPrefix = config.idPrefix || ''\n const loaderImports: string[] = []\n const componentEntries: string[] = []\n\n for (const [id, entry] of Object.entries(config.components)) {\n const loaderStr = entry.loader.toString()\n const importMatch = loaderStr.match(/import\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)/)\n\n if (importMatch) {\n const importPath = importMatch[1]\n const prefixedId = idPrefix + id\n loaderImports.push(` '${prefixedId}': () => import('${importPath}'),`)\n\n // Build component entry with optional transformProps\n const entryParts = [\n ` path: '${entry.path}'`,\n ` loader: loaders['${prefixedId}']`,\n ]\n if (entry.transformProps) {\n entryParts.push(` transformProps: ${entry.transformProps.toString()}`)\n }\n\n componentEntries.push(` '${prefixedId}': {\\n${entryParts.join(',\\n')},\\n },`)\n }\n }\n\n return `import { createRenderFunction } from 'drupal-canvas-react/preview'\n\nconst loaders = {\n${loaderImports.join('\\n')}\n}\n\nconst components = {\n${componentEntries.join('\\n')}\n}\n\nexport const render = createRenderFunction(components)\n\nif (typeof window !== 'undefined') {\n ;(window as any).render = render\n}\n`\n}\n\nasync function bundle(cwd: string) {\n const config = await loadConfig(cwd)\n const outputFilename = config.outputFilename || 'drupal-canvas.js'\n\n // Generate temporary entry file\n const entryContent = generateEntryCode(config)\n const tempEntryPath = path.join(cwd, '.drupal-canvas-entry.ts')\n fs.writeFileSync(tempEntryPath, entryContent)\n\n try {\n const tsupConfig = createTsupConfig(config, { cwd, entry: tempEntryPath, outputFilename })\n\n const tsup = await import('tsup')\n await tsup.build(tsupConfig)\n console.log(`Built ${outputFilename} to ${config.outDir}`)\n } finally {\n // Clean up temp file\n if (fs.existsSync(tempEntryPath)) {\n fs.unlinkSync(tempEntryPath)\n }\n }\n}\n\nasync function build(cwd: string) {\n console.log('Generating component index...')\n await generateIndex(cwd)\n\n console.log('\\nBuilding standalone bundle...')\n await bundle(cwd)\n\n console.log('\\nBuild complete!')\n}\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n","import * as fs from 'fs'\nimport * as path from 'path'\nimport { pathToFileURL } from 'url'\nimport type { CanvasConfig } from './types.js'\n\n/**\n * Helper to define a config with type checking.\n */\nexport function defineConfig(config: CanvasConfig): CanvasConfig {\n return config\n}\n\n/**\n * Validate that required config fields are present.\n */\nfunction validateConfig(config: unknown, source: string): CanvasConfig {\n if (!config || typeof config !== 'object') {\n throw new Error(`Invalid config from ${source}: must be an object`)\n }\n\n const cfg = config as Record<string, unknown>\n\n if (typeof cfg.outDir !== 'string') {\n throw new Error(`Invalid config from ${source}: outDir is required and must be a string`)\n }\n\n if (!cfg.components || typeof cfg.components !== 'object') {\n throw new Error(`Invalid config from ${source}: components is required and must be an object`)\n }\n\n return config as CanvasConfig\n}\n\n/**\n * Load config from canvas.config.{mts,ts,mjs,js} or package.json.\n *\n * @param cwd - Working directory to search for config. Defaults to process.cwd().\n * @returns The loaded and validated config.\n */\nexport async function loadConfig(cwd: string = process.cwd()): Promise<CanvasConfig> {\n // Try config files in order of preference\n const configFiles = [\n 'canvas.config.mts', // ESM TypeScript (no warning)\n 'canvas.config.ts',\n 'canvas.config.mjs', // ESM JavaScript\n 'canvas.config.js',\n ]\n\n for (const filename of configFiles) {\n const configPath = path.join(cwd, filename)\n if (fs.existsSync(configPath)) {\n const module = await import(pathToFileURL(configPath).href)\n const config = module.default || module\n return validateConfig(config, configPath)\n }\n }\n\n // Try package.json drupal-canvas field\n const packageJsonPath = path.join(cwd, 'package.json')\n if (fs.existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n if (packageJson['drupal-canvas']) {\n return validateConfig(packageJson['drupal-canvas'], 'package.json#drupal-canvas')\n }\n }\n\n throw new Error(\n `No config found. Create canvas.config.mts (recommended), canvas.config.ts, or add \"drupal-canvas\" to package.json`\n )\n}\n\n/**\n * Resolve a path relative to the config file location.\n *\n * @param configDir - Directory containing the config file.\n * @param relativePath - Path relative to the config file.\n * @returns Absolute path.\n */\nexport function resolveConfigPath(configDir: string, relativePath: string): string {\n return path.resolve(configDir, relativePath)\n}\n","import { parse } from 'react-docgen-typescript'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport type { CanvasConfig, ComponentIndex, PropertySchema, OutputPropertySchema, SlotDefinition } from './types.js'\n\nconst CANVAS_IMAGE_REF = 'json-schema-definitions://canvas.module/image'\n\n/**\n * Transform a PropertySchema to OutputPropertySchema for the component index.\n * Converts 'image' type to Canvas's expected format.\n */\nfunction toOutputProperty(prop: PropertySchema): OutputPropertySchema {\n if (prop.type === 'image') {\n return {\n type: 'object',\n title: prop.title,\n description: prop.description,\n $ref: CANVAS_IMAGE_REF,\n }\n }\n return prop as OutputPropertySchema\n}\n\ninterface GenerateIndexOptions {\n /**\n * Working directory for resolving paths.\n */\n cwd?: string\n}\n\n/**\n * Convert a TypeScript type name to JSON Schema type.\n * Returns null if the type is incompatible with Canvas (objects, arrays).\n */\nfunction convertTypeToJsonSchema(\n typeName: string\n): { type: 'string' | 'number' | 'boolean'; incompatible?: undefined } | { incompatible: true } {\n // Primitive types are compatible\n if (typeName === 'number') return { type: 'number' }\n if (typeName === 'boolean') return { type: 'boolean' }\n if (typeName === 'string') return { type: 'string' }\n\n // Enum/union of string literals is compatible (will be type: 'string' with enum)\n if (typeName === 'enum' || /^[\"']/.test(typeName)) return { type: 'string' }\n\n // Object types (contains { or is a named type that's not a primitive)\n if (typeName.includes('{') || typeName.includes('[')) {\n return { incompatible: true }\n }\n\n // Named types (like DrupalImage, ImageTypes, etc.) are likely objects\n // Exception: some types might be string aliases, but we can't know for sure\n if (/^[A-Z]/.test(typeName)) {\n return { incompatible: true }\n }\n\n // Default to string for anything else\n return { type: 'string' }\n}\n\n/**\n * Extract enum values from a type string like '\"S\" | \"M\" | \"L\"' or \"'S' | 'M' | 'L'\".\n */\nfunction extractEnumValues(typeString: string | undefined): string[] | undefined {\n if (!typeString) return undefined\n\n // Match quoted string values in union types (double or single quotes)\n const matches = typeString.match(/[\"']([^\"']+)[\"']/g)\n if (!matches || matches.length === 0) return undefined\n\n // Remove quotes from matched values\n return matches.map((m) => m.slice(1, -1))\n}\n\n/**\n * Extract enum values for a prop directly from source code to preserve original order.\n * Looks for patterns like: propName?: 'S' | 'M' | 'L' or propName: \"A\" | \"B\"\n */\nfunction extractEnumFromSource(sourceCode: string, propName: string): string[] | undefined {\n // Match prop definition with union of string literals\n // Handles: propName?: 'A' | 'B' | 'C' or propName: \"X\" | \"Y\"\n const pattern = new RegExp(\n `${propName}\\\\??:\\\\s*(['\"][^'\"]+['\"](?:\\\\s*\\\\|\\\\s*['\"][^'\"]+['\"])*)`,\n 'm'\n )\n const match = sourceCode.match(pattern)\n if (!match) return undefined\n\n return extractEnumValues(match[1])\n}\n\n/**\n * Extract the order of props from source code.\n * Returns prop names in the order they appear in the type definition.\n */\nfunction extractPropOrder(sourceCode: string): string[] {\n // Match prop definitions like: propName?: type or propName: type\n const propPattern = /^\\s*(\\w+)\\??:/gm\n const props: string[] = []\n let match\n while ((match = propPattern.exec(sourceCode)) !== null) {\n if (!props.includes(match[1])) {\n props.push(match[1])\n }\n }\n return props\n}\n\n/**\n * Sort properties object to match source order.\n */\nfunction sortPropertiesBySourceOrder(\n properties: Record<string, PropertySchema>,\n sourceOrder: string[]\n): Record<string, PropertySchema> {\n const sorted: Record<string, PropertySchema> = {}\n\n // First add props in source order\n for (const propName of sourceOrder) {\n if (properties[propName]) {\n sorted[propName] = properties[propName]\n }\n }\n\n // Then add any remaining props not in source (shouldn't happen normally)\n for (const propName of Object.keys(properties)) {\n if (!sorted[propName]) {\n sorted[propName] = properties[propName]\n }\n }\n\n return sorted\n}\n\n/**\n * Parse a JSDoc comment to extract title and description.\n *\n * Supports \"Title: Description\" format, otherwise uses camelCase to sentence case conversion.\n */\nfunction parseTitleAndDescription(\n comment: string | undefined,\n propName: string\n): { title: string; description: string | undefined } {\n // Convert camelCase to sentence case (e.g., fullWidth -> Full width)\n const defaultTitle = propName\n .replace(/([A-Z])/g, ' $1')\n .toLowerCase()\n .replace(/^./, (c) => c.toUpperCase())\n .trim()\n\n if (!comment) {\n return { title: defaultTitle, description: undefined }\n }\n\n // Check for \"Title: Description\" format\n const match = comment.match(/^([^:]+):\\s*(.*)$/)\n if (match) {\n return {\n title: match[1].trim(),\n description: match[2].trim() || undefined,\n }\n }\n\n return { title: defaultTitle, description: comment }\n}\n\n/**\n * Generate the component index from the component map.\n *\n * @param config - The canvas config.\n * @param options - Generation options.\n * @returns The generated component index.\n */\n/**\n * Check if a type string indicates a ReactNode (slot) type.\n */\nfunction isReactNodeType(typeName: string): boolean {\n return (\n typeName === 'ReactNode' ||\n typeName.includes('ReactNode') ||\n typeName === 'React.ReactNode' ||\n typeName.includes('React.ReactNode')\n )\n}\n\nexport async function generateComponentIndex(\n config: CanvasConfig,\n options: GenerateIndexOptions = {}\n): Promise<ComponentIndex> {\n const cwd = options.cwd || process.cwd()\n const defaultCategory = config.defaultCategory || 'Components'\n const idPrefix = config.idPrefix || ''\n\n // Use components directly from config\n const components = config.components\n\n const result = []\n\n for (const [id, entry] of Object.entries(components)) {\n const filePath = path.resolve(cwd, entry.path)\n\n // Parse TypeScript props\n const parsed = parse(filePath, {\n shouldExtractLiteralValuesFromEnum: false,\n shouldRemoveUndefinedFromOptional: true,\n })\n\n const componentDoc = parsed.length > 0 ? parsed[0] : null\n const sourceCode = fs.readFileSync(filePath, 'utf-8')\n\n // Check if we have enough info to include this component\n const hasManualMeta = entry.name || entry.description || entry.props || entry.slots\n if (!componentDoc && !hasManualMeta) {\n console.warn(`No component found in ${filePath} (add name/description to config to include)`)\n continue\n }\n\n // Build meta from available sources (entry > auto-generated)\n const meta = {\n name: entry.name || componentDoc?.displayName || id,\n description: entry.description || componentDoc?.description || `${id} component`,\n }\n\n // Convert props to JSON Schema format, auto-detecting slots\n const properties: Record<string, PropertySchema> = {}\n const slots: Record<string, SlotDefinition> = {}\n\n for (const [propName, propInfo] of Object.entries(componentDoc?.props ?? {})) {\n // Skip props inherited from node_modules (e.g., React.HTMLAttributes)\n // Allow props from the same project (src/) even if via type helpers\n const parent = (propInfo as any).parent\n if (parent && parent.fileName.includes('node_modules')) {\n continue\n }\n\n const { title, description } = parseTitleAndDescription(propInfo.description, propName)\n\n // Auto-detect slots from ReactNode type\n if (isReactNodeType(propInfo.type.name)) {\n slots[propName] = {\n title,\n description,\n }\n continue\n }\n\n // Check if the type is compatible with Canvas\n const typeResult = convertTypeToJsonSchema(propInfo.type.name)\n if (typeResult.incompatible) {\n console.warn(\n ` ⚠ ${id}.${propName}: type \"${propInfo.type.name}\" is not supported`\n )\n continue\n }\n\n // Extract enum values from source if this looks like a union of string literals\n // Check if type.name contains quoted strings with | (e.g., \"L\" | \"S\" | \"M\")\n const looksLikeEnum = /[\"'][^\"']+[\"']\\s*\\|/.test(propInfo.type.name)\n const enumValues = looksLikeEnum ? extractEnumFromSource(sourceCode, propName) : undefined\n\n properties[propName] = {\n type: typeResult.type,\n title,\n description,\n default: propInfo.defaultValue?.value,\n enum: enumValues,\n }\n }\n\n // Merge manual props/slots from config (manual takes precedence)\n // If a prop is manually defined, remove it from slots (can't be both)\n const mergedProperties: Record<string, PropertySchema> = { ...properties }\n for (const [propName, propValue] of Object.entries(entry.props ?? {})) {\n if (propValue === false) {\n // Exclude this prop from output\n delete mergedProperties[propName]\n } else {\n mergedProperties[propName] = propValue\n }\n delete slots[propName]\n }\n const mergedSlots = { ...slots, ...entry.slots }\n\n // Sort properties to match source order\n const propOrder = extractPropOrder(sourceCode)\n const sortedProperties = sortPropertiesBySourceOrder(mergedProperties, propOrder)\n\n // Transform properties to output format (e.g., image → object with $ref)\n const outputProperties: Record<string, OutputPropertySchema> = {}\n for (const [propName, prop] of Object.entries(sortedProperties)) {\n outputProperties[propName] = toOutputProperty(prop)\n }\n\n result.push({\n id: idPrefix + id,\n name: meta.name,\n category: entry.category || defaultCategory,\n description: meta.description,\n status: 'stable' as const,\n props: {\n type: 'object' as const,\n properties: outputProperties,\n },\n slots: Object.keys(mergedSlots).length > 0 ? mergedSlots : undefined,\n })\n }\n\n return {\n version: '1.0',\n components: result,\n }\n}\n\n/**\n * Generate and write the component index to the output directory.\n *\n * @param config - The canvas config.\n * @param options - Generation options.\n */\nexport async function writeComponentIndex(\n config: CanvasConfig,\n options: GenerateIndexOptions = {}\n): Promise<void> {\n const cwd = options.cwd || process.cwd()\n const index = await generateComponentIndex(config, options)\n\n const outDir = path.resolve(cwd, config.outDir)\n\n // Ensure output directory exists\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true })\n }\n\n const outputPath = path.join(outDir, 'component-index.json')\n fs.writeFileSync(outputPath, JSON.stringify(index, null, 2) + '\\n')\n console.log(`Generated ${outputPath}`)\n}\n","import { createRequire } from 'module'\nimport * as path from 'path'\nimport type { Options } from 'tsup'\nimport type { CanvasConfig } from './types.js'\n\nconst require = createRequire(import.meta.url)\n\n/**\n * Get the path to the built-in stubs directory.\n * This resolves to the dist/stubs directory in the package.\n */\nfunction getBuiltinStubsDir(): string {\n // When running from dist, __dirname points to dist/\n // The stubs are at dist/stubs/\n return path.join(path.dirname(new URL(import.meta.url).pathname), 'stubs')\n}\n\ninterface CreateTsupConfigOptions {\n /**\n * Working directory for resolving paths.\n */\n cwd?: string\n\n /**\n * Entry file path for the standalone bundle.\n */\n entry?: string\n\n /**\n * Output filename for the bundle.\n * @default 'drupal-canvas.js'\n */\n outputFilename?: string\n}\n\n/**\n * Create a tsup configuration for building the standalone bundle.\n *\n * @param config - The canvas config.\n * @param options - Additional options.\n * @returns A tsup configuration object.\n *\n * @example\n * ```ts\n * // tsup.config.ts\n * import { createTsupConfig, loadConfig } from 'drupal-canvas-react'\n *\n * export default loadConfig().then(createTsupConfig)\n * ```\n */\nexport function createTsupConfig(\n config: CanvasConfig,\n options: CreateTsupConfigOptions = {}\n): Options {\n const cwd = options.cwd || process.cwd()\n const builtinStubsDir = getBuiltinStubsDir()\n\n // Resolve paths\n const entryFile = options.entry\n if (!entryFile) {\n throw new Error('Entry file is required')\n }\n const outDir = path.resolve(cwd, config.outDir)\n const outputFilename = options.outputFilename || 'drupal-canvas.js'\n\n // Build alias map\n // Include react/react-dom to dedupe and prevent multiple React instances\n // Use package directory (not main file) so sub-imports like react/jsx-runtime resolve\n const reactDir = path.dirname(require.resolve('react/package.json'))\n const reactDomDir = path.dirname(require.resolve('react-dom/package.json'))\n const alias: Record<string, string> = {\n react: reactDir,\n 'react-dom': reactDomDir,\n 'next/image': path.join(builtinStubsDir, 'image.js'),\n 'next/link': path.join(builtinStubsDir, 'link.js'),\n }\n\n // Add custom stubs from config\n if (config.stubs) {\n for (const [moduleName, stubPath] of Object.entries(config.stubs)) {\n alias[moduleName] = path.resolve(cwd, stubPath)\n }\n }\n\n // Remove .js extension if present for outfile naming\n const baseName = outputFilename.replace(/\\.js$/, '')\n\n return {\n entry: { [baseName]: entryFile },\n format: 'esm',\n platform: 'browser',\n target: 'es2020',\n outDir,\n outExtension: () => ({ js: '.js' }),\n noExternal: [/.*/],\n minify: config.minify !== false,\n splitting: false,\n tsconfig: config.tsconfig ? path.resolve(cwd, config.tsconfig) : undefined,\n esbuildPlugins: config.esbuildPlugins,\n esbuildOptions(esbuildOptions) {\n esbuildOptions.alias = alias\n },\n }\n}\n"],"mappings":";;;AAEA,YAAYA,SAAQ;AACpB,YAAYC,WAAU;;;ACHtB,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,qBAAqB;AAa9B,SAAS,eAAe,QAAiB,QAA8B;AACrE,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,UAAM,IAAI,MAAM,uBAAuB,MAAM,qBAAqB;AAAA,EACpE;AAEA,QAAM,MAAM;AAEZ,MAAI,OAAO,IAAI,WAAW,UAAU;AAClC,UAAM,IAAI,MAAM,uBAAuB,MAAM,2CAA2C;AAAA,EAC1F;AAEA,MAAI,CAAC,IAAI,cAAc,OAAO,IAAI,eAAe,UAAU;AACzD,UAAM,IAAI,MAAM,uBAAuB,MAAM,gDAAgD;AAAA,EAC/F;AAEA,SAAO;AACT;AAQA,eAAsB,WAAW,MAAc,QAAQ,IAAI,GAA0B;AAEnF,QAAM,cAAc;AAAA,IAClB;AAAA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IACA;AAAA,EACF;AAEA,aAAW,YAAY,aAAa;AAClC,UAAM,aAAkB,UAAK,KAAK,QAAQ;AAC1C,QAAO,cAAW,UAAU,GAAG;AAC7B,YAAM,SAAS,MAAM,OAAO,cAAc,UAAU,EAAE;AACtD,YAAM,SAAS,OAAO,WAAW;AACjC,aAAO,eAAe,QAAQ,UAAU;AAAA,IAC1C;AAAA,EACF;AAGA,QAAM,kBAAuB,UAAK,KAAK,cAAc;AACrD,MAAO,cAAW,eAAe,GAAG;AAClC,UAAM,cAAc,KAAK,MAAS,gBAAa,iBAAiB,OAAO,CAAC;AACxE,QAAI,YAAY,eAAe,GAAG;AAChC,aAAO,eAAe,YAAY,eAAe,GAAG,4BAA4B;AAAA,IAClF;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;;;ACrEA,SAAS,aAAa;AACtB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAGtB,IAAM,mBAAmB;AAMzB,SAAS,iBAAiB,MAA4C;AACpE,MAAI,KAAK,SAAS,SAAS;AACzB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK;AAAA,MAClB,MAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO;AACT;AAaA,SAAS,wBACP,UAC8F;AAE9F,MAAI,aAAa,SAAU,QAAO,EAAE,MAAM,SAAS;AACnD,MAAI,aAAa,UAAW,QAAO,EAAE,MAAM,UAAU;AACrD,MAAI,aAAa,SAAU,QAAO,EAAE,MAAM,SAAS;AAGnD,MAAI,aAAa,UAAU,QAAQ,KAAK,QAAQ,EAAG,QAAO,EAAE,MAAM,SAAS;AAG3E,MAAI,SAAS,SAAS,GAAG,KAAK,SAAS,SAAS,GAAG,GAAG;AACpD,WAAO,EAAE,cAAc,KAAK;AAAA,EAC9B;AAIA,MAAI,SAAS,KAAK,QAAQ,GAAG;AAC3B,WAAO,EAAE,cAAc,KAAK;AAAA,EAC9B;AAGA,SAAO,EAAE,MAAM,SAAS;AAC1B;AAKA,SAAS,kBAAkB,YAAsD;AAC/E,MAAI,CAAC,WAAY,QAAO;AAGxB,QAAM,UAAU,WAAW,MAAM,mBAAmB;AACpD,MAAI,CAAC,WAAW,QAAQ,WAAW,EAAG,QAAO;AAG7C,SAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC;AAC1C;AAMA,SAAS,sBAAsB,YAAoB,UAAwC;AAGzF,QAAM,UAAU,IAAI;AAAA,IAClB,GAAG,QAAQ;AAAA,IACX;AAAA,EACF;AACA,QAAM,QAAQ,WAAW,MAAM,OAAO;AACtC,MAAI,CAAC,MAAO,QAAO;AAEnB,SAAO,kBAAkB,MAAM,CAAC,CAAC;AACnC;AAMA,SAAS,iBAAiB,YAA8B;AAEtD,QAAM,cAAc;AACpB,QAAM,QAAkB,CAAC;AACzB,MAAI;AACJ,UAAQ,QAAQ,YAAY,KAAK,UAAU,OAAO,MAAM;AACtD,QAAI,CAAC,MAAM,SAAS,MAAM,CAAC,CAAC,GAAG;AAC7B,YAAM,KAAK,MAAM,CAAC,CAAC;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,4BACP,YACA,aACgC;AAChC,QAAM,SAAyC,CAAC;AAGhD,aAAW,YAAY,aAAa;AAClC,QAAI,WAAW,QAAQ,GAAG;AACxB,aAAO,QAAQ,IAAI,WAAW,QAAQ;AAAA,IACxC;AAAA,EACF;AAGA,aAAW,YAAY,OAAO,KAAK,UAAU,GAAG;AAC9C,QAAI,CAAC,OAAO,QAAQ,GAAG;AACrB,aAAO,QAAQ,IAAI,WAAW,QAAQ;AAAA,IACxC;AAAA,EACF;AAEA,SAAO;AACT;AAOA,SAAS,yBACP,SACA,UACoD;AAEpD,QAAM,eAAe,SAClB,QAAQ,YAAY,KAAK,EACzB,YAAY,EACZ,QAAQ,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EACpC,KAAK;AAER,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,OAAO,cAAc,aAAa,OAAU;AAAA,EACvD;AAGA,QAAM,QAAQ,QAAQ,MAAM,mBAAmB;AAC/C,MAAI,OAAO;AACT,WAAO;AAAA,MACL,OAAO,MAAM,CAAC,EAAE,KAAK;AAAA,MACrB,aAAa,MAAM,CAAC,EAAE,KAAK,KAAK;AAAA,IAClC;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,cAAc,aAAa,QAAQ;AACrD;AAYA,SAAS,gBAAgB,UAA2B;AAClD,SACE,aAAa,eACb,SAAS,SAAS,WAAW,KAC7B,aAAa,qBACb,SAAS,SAAS,iBAAiB;AAEvC;AAEA,eAAsB,uBACpB,QACA,UAAgC,CAAC,GACR;AACzB,QAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;AACvC,QAAM,kBAAkB,OAAO,mBAAmB;AAClD,QAAM,WAAW,OAAO,YAAY;AAGpC,QAAM,aAAa,OAAO;AAE1B,QAAM,SAAS,CAAC;AAEhB,aAAW,CAAC,IAAI,KAAK,KAAK,OAAO,QAAQ,UAAU,GAAG;AACpD,UAAM,WAAgB,cAAQ,KAAK,MAAM,IAAI;AAG7C,UAAM,SAAS,MAAM,UAAU;AAAA,MAC7B,oCAAoC;AAAA,MACpC,mCAAmC;AAAA,IACrC,CAAC;AAED,UAAM,eAAe,OAAO,SAAS,IAAI,OAAO,CAAC,IAAI;AACrD,UAAM,aAAgB,iBAAa,UAAU,OAAO;AAGpD,UAAM,gBAAgB,MAAM,QAAQ,MAAM,eAAe,MAAM,SAAS,MAAM;AAC9E,QAAI,CAAC,gBAAgB,CAAC,eAAe;AACnC,cAAQ,KAAK,yBAAyB,QAAQ,8CAA8C;AAC5F;AAAA,IACF;AAGA,UAAM,OAAO;AAAA,MACX,MAAM,MAAM,QAAQ,cAAc,eAAe;AAAA,MACjD,aAAa,MAAM,eAAe,cAAc,eAAe,GAAG,EAAE;AAAA,IACtE;AAGA,UAAM,aAA6C,CAAC;AACpD,UAAM,QAAwC,CAAC;AAE/C,eAAW,CAAC,UAAU,QAAQ,KAAK,OAAO,QAAQ,cAAc,SAAS,CAAC,CAAC,GAAG;AAG5E,YAAM,SAAU,SAAiB;AACjC,UAAI,UAAU,OAAO,SAAS,SAAS,cAAc,GAAG;AACtD;AAAA,MACF;AAEA,YAAM,EAAE,OAAO,YAAY,IAAI,yBAAyB,SAAS,aAAa,QAAQ;AAGtF,UAAI,gBAAgB,SAAS,KAAK,IAAI,GAAG;AACvC,cAAM,QAAQ,IAAI;AAAA,UAChB;AAAA,UACA;AAAA,QACF;AACA;AAAA,MACF;AAGA,YAAM,aAAa,wBAAwB,SAAS,KAAK,IAAI;AAC7D,UAAI,WAAW,cAAc;AAC3B,gBAAQ;AAAA,UACN,YAAO,EAAE,IAAI,QAAQ,WAAW,SAAS,KAAK,IAAI;AAAA,QACpD;AACA;AAAA,MACF;AAIA,YAAM,gBAAgB,sBAAsB,KAAK,SAAS,KAAK,IAAI;AACnE,YAAM,aAAa,gBAAgB,sBAAsB,YAAY,QAAQ,IAAI;AAEjF,iBAAW,QAAQ,IAAI;AAAA,QACrB,MAAM,WAAW;AAAA,QACjB;AAAA,QACA;AAAA,QACA,SAAS,SAAS,cAAc;AAAA,QAChC,MAAM;AAAA,MACR;AAAA,IACF;AAIA,UAAM,mBAAmD,EAAE,GAAG,WAAW;AACzE,eAAW,CAAC,UAAU,SAAS,KAAK,OAAO,QAAQ,MAAM,SAAS,CAAC,CAAC,GAAG;AACrE,UAAI,cAAc,OAAO;AAEvB,eAAO,iBAAiB,QAAQ;AAAA,MAClC,OAAO;AACL,yBAAiB,QAAQ,IAAI;AAAA,MAC/B;AACA,aAAO,MAAM,QAAQ;AAAA,IACvB;AACA,UAAM,cAAc,EAAE,GAAG,OAAO,GAAG,MAAM,MAAM;AAG/C,UAAM,YAAY,iBAAiB,UAAU;AAC7C,UAAM,mBAAmB,4BAA4B,kBAAkB,SAAS;AAGhF,UAAM,mBAAyD,CAAC;AAChE,eAAW,CAAC,UAAU,IAAI,KAAK,OAAO,QAAQ,gBAAgB,GAAG;AAC/D,uBAAiB,QAAQ,IAAI,iBAAiB,IAAI;AAAA,IACpD;AAEA,WAAO,KAAK;AAAA,MACV,IAAI,WAAW;AAAA,MACf,MAAM,KAAK;AAAA,MACX,UAAU,MAAM,YAAY;AAAA,MAC5B,aAAa,KAAK;AAAA,MAClB,QAAQ;AAAA,MACR,OAAO;AAAA,QACL,MAAM;AAAA,QACN,YAAY;AAAA,MACd;AAAA,MACA,OAAO,OAAO,KAAK,WAAW,EAAE,SAAS,IAAI,cAAc;AAAA,IAC7D,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,YAAY;AAAA,EACd;AACF;AAQA,eAAsB,oBACpB,QACA,UAAgC,CAAC,GAClB;AACf,QAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;AACvC,QAAM,QAAQ,MAAM,uBAAuB,QAAQ,OAAO;AAE1D,QAAM,SAAc,cAAQ,KAAK,OAAO,MAAM;AAG9C,MAAI,CAAI,eAAW,MAAM,GAAG;AAC1B,IAAG,cAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AAAA,EAC1C;AAEA,QAAM,aAAkB,WAAK,QAAQ,sBAAsB;AAC3D,EAAG,kBAAc,YAAY,KAAK,UAAU,OAAO,MAAM,CAAC,IAAI,IAAI;AAClE,UAAQ,IAAI,aAAa,UAAU,EAAE;AACvC;;;AChVA,SAAS,qBAAqB;AAC9B,YAAYC,WAAU;AAItB,IAAMC,WAAU,cAAc,YAAY,GAAG;AAM7C,SAAS,qBAA6B;AAGpC,SAAY,WAAU,cAAQ,IAAI,IAAI,YAAY,GAAG,EAAE,QAAQ,GAAG,OAAO;AAC3E;AAmCO,SAAS,iBACd,QACA,UAAmC,CAAC,GAC3B;AACT,QAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;AACvC,QAAM,kBAAkB,mBAAmB;AAG3C,QAAM,YAAY,QAAQ;AAC1B,MAAI,CAAC,WAAW;AACd,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,QAAM,SAAc,cAAQ,KAAK,OAAO,MAAM;AAC9C,QAAM,iBAAiB,QAAQ,kBAAkB;AAKjD,QAAM,WAAgB,cAAQA,SAAQ,QAAQ,oBAAoB,CAAC;AACnE,QAAM,cAAmB,cAAQA,SAAQ,QAAQ,wBAAwB,CAAC;AAC1E,QAAM,QAAgC;AAAA,IACpC,OAAO;AAAA,IACP,aAAa;AAAA,IACb,cAAmB,WAAK,iBAAiB,UAAU;AAAA,IACnD,aAAkB,WAAK,iBAAiB,SAAS;AAAA,EACnD;AAGA,MAAI,OAAO,OAAO;AAChB,eAAW,CAAC,YAAY,QAAQ,KAAK,OAAO,QAAQ,OAAO,KAAK,GAAG;AACjE,YAAM,UAAU,IAAS,cAAQ,KAAK,QAAQ;AAAA,IAChD;AAAA,EACF;AAGA,QAAM,WAAW,eAAe,QAAQ,SAAS,EAAE;AAEnD,SAAO;AAAA,IACL,OAAO,EAAE,CAAC,QAAQ,GAAG,UAAU;AAAA,IAC/B,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,QAAQ;AAAA,IACR;AAAA,IACA,cAAc,OAAO,EAAE,IAAI,MAAM;AAAA,IACjC,YAAY,CAAC,IAAI;AAAA,IACjB,QAAQ,OAAO,WAAW;AAAA,IAC1B,WAAW;AAAA,IACX,UAAU,OAAO,WAAgB,cAAQ,KAAK,OAAO,QAAQ,IAAI;AAAA,IACjE,gBAAgB,OAAO;AAAA,IACvB,eAAe,gBAAgB;AAC7B,qBAAe,QAAQ;AAAA,IACzB;AAAA,EACF;AACF;;;AH9FA,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBb,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+BxB,eAAe,OAAO;AACpB,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,QAAM,UAAU,KAAK,CAAC;AAEtB,MAAI,CAAC,WAAW,YAAY,YAAY,YAAY,MAAM;AACxD,YAAQ,IAAI,IAAI;AAChB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,YAAY,eAAe,YAAY,MAAM;AAC/C,UAAM,kBAAuB,WAAU,cAAQ,IAAI,IAAI,YAAY,GAAG,EAAE,QAAQ,GAAG,MAAM,cAAc;AACvG,UAAM,cAAc,KAAK,MAAS,iBAAa,iBAAiB,OAAO,CAAC;AACxE,YAAQ,IAAI,YAAY,OAAO;AAC/B,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,QAAQ,IAAI;AAExB,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,YAAM,KAAK,GAAG;AACd;AAAA,IAEF,KAAK;AACH,YAAM,cAAc,GAAG;AACvB;AAAA,IAEF,KAAK;AACH,YAAM,OAAO,GAAG;AAChB;AAAA,IAEF,KAAK;AACH,YAAM,MAAM,GAAG;AACf;AAAA,IAEF;AACE,cAAQ,MAAM,oBAAoB,OAAO,EAAE;AAC3C,cAAQ,IAAI,IAAI;AAChB,cAAQ,KAAK,CAAC;AAAA,EAClB;AACF;AAEA,eAAe,KAAK,KAAa;AAC/B,QAAM,aAAkB,WAAK,KAAK,kBAAkB;AAEpD,MAAO,eAAW,UAAU,GAAG;AAC7B,YAAQ,MAAM,sCAAsC,UAAU,EAAE;AAChE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,EAAG,kBAAc,YAAY,eAAe;AAC5C,UAAQ,IAAI,WAAW,UAAU,EAAE;AACnC,UAAQ,IAAI,eAAe;AAC3B,UAAQ,IAAI,iDAAiD;AAC7D,UAAQ,IAAI,mFAAmF;AACjG;AAEA,eAAe,cAAc,KAAa;AACxC,QAAM,SAAS,MAAM,WAAW,GAAG;AACnC,QAAM,oBAAoB,QAAQ,EAAE,IAAI,CAAC;AAC3C;AAEA,SAAS,kBAAkB,QAA8B;AACvD,QAAM,WAAW,OAAO,YAAY;AACpC,QAAM,gBAA0B,CAAC;AACjC,QAAM,mBAA6B,CAAC;AAEpC,aAAW,CAAC,IAAI,KAAK,KAAK,OAAO,QAAQ,OAAO,UAAU,GAAG;AAC3D,UAAM,YAAY,MAAM,OAAO,SAAS;AACxC,UAAM,cAAc,UAAU,MAAM,qCAAqC;AAEzE,QAAI,aAAa;AACf,YAAM,aAAa,YAAY,CAAC;AAChC,YAAM,aAAa,WAAW;AAC9B,oBAAc,KAAK,QAAQ,UAAU,oBAAoB,UAAU,KAAK;AAGxE,YAAM,aAAa;AAAA,QACjB,cAAc,MAAM,IAAI;AAAA,QACxB,wBAAwB,UAAU;AAAA,MACpC;AACA,UAAI,MAAM,gBAAgB;AACxB,mBAAW,KAAK,uBAAuB,MAAM,eAAe,SAAS,CAAC,EAAE;AAAA,MAC1E;AAEA,uBAAiB,KAAK,MAAM,UAAU;AAAA,EAAS,WAAW,KAAK,KAAK,CAAC;AAAA,KAAS;AAAA,IAChF;AAAA,EACF;AAEA,SAAO;AAAA;AAAA;AAAA,EAGP,cAAc,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,EAIxB,iBAAiB,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAS7B;AAEA,eAAe,OAAO,KAAa;AACjC,QAAM,SAAS,MAAM,WAAW,GAAG;AACnC,QAAM,iBAAiB,OAAO,kBAAkB;AAGhD,QAAM,eAAe,kBAAkB,MAAM;AAC7C,QAAM,gBAAqB,WAAK,KAAK,yBAAyB;AAC9D,EAAG,kBAAc,eAAe,YAAY;AAE5C,MAAI;AACF,UAAM,aAAa,iBAAiB,QAAQ,EAAE,KAAK,OAAO,eAAe,eAAe,CAAC;AAEzF,UAAM,OAAO,MAAM,OAAO,MAAM;AAChC,UAAM,KAAK,MAAM,UAAU;AAC3B,YAAQ,IAAI,SAAS,cAAc,OAAO,OAAO,MAAM,EAAE;AAAA,EAC3D,UAAE;AAEA,QAAO,eAAW,aAAa,GAAG;AAChC,MAAG,eAAW,aAAa;AAAA,IAC7B;AAAA,EACF;AACF;AAEA,eAAe,MAAM,KAAa;AAChC,UAAQ,IAAI,+BAA+B;AAC3C,QAAM,cAAc,GAAG;AAEvB,UAAQ,IAAI,iCAAiC;AAC7C,QAAM,OAAO,GAAG;AAEhB,UAAQ,IAAI,mBAAmB;AACjC;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["fs","path","fs","path","path","require"]}
@@ -0,0 +1,74 @@
1
+ import { C as CanvasConfig, a as ComponentIndex } from './types-DE6lplQu.js';
2
+ export { b as ComponentEntry, c as ComponentMap, P as PropertySchema, S as SlotDefinition } from './types-DE6lplQu.js';
3
+ import { Options } from 'tsup';
4
+ import 'esbuild';
5
+ import 'react';
6
+
7
+ /**
8
+ * Helper to define a config with type checking.
9
+ */
10
+ declare function defineConfig(config: CanvasConfig): CanvasConfig;
11
+ /**
12
+ * Load config from canvas.config.{mts,ts,mjs,js} or package.json.
13
+ *
14
+ * @param cwd - Working directory to search for config. Defaults to process.cwd().
15
+ * @returns The loaded and validated config.
16
+ */
17
+ declare function loadConfig(cwd?: string): Promise<CanvasConfig>;
18
+ /**
19
+ * Resolve a path relative to the config file location.
20
+ *
21
+ * @param configDir - Directory containing the config file.
22
+ * @param relativePath - Path relative to the config file.
23
+ * @returns Absolute path.
24
+ */
25
+ declare function resolveConfigPath(configDir: string, relativePath: string): string;
26
+
27
+ interface GenerateIndexOptions {
28
+ /**
29
+ * Working directory for resolving paths.
30
+ */
31
+ cwd?: string;
32
+ }
33
+ declare function generateComponentIndex(config: CanvasConfig, options?: GenerateIndexOptions): Promise<ComponentIndex>;
34
+ /**
35
+ * Generate and write the component index to the output directory.
36
+ *
37
+ * @param config - The canvas config.
38
+ * @param options - Generation options.
39
+ */
40
+ declare function writeComponentIndex(config: CanvasConfig, options?: GenerateIndexOptions): Promise<void>;
41
+
42
+ interface CreateTsupConfigOptions {
43
+ /**
44
+ * Working directory for resolving paths.
45
+ */
46
+ cwd?: string;
47
+ /**
48
+ * Entry file path for the standalone bundle.
49
+ */
50
+ entry?: string;
51
+ /**
52
+ * Output filename for the bundle.
53
+ * @default 'drupal-canvas.js'
54
+ */
55
+ outputFilename?: string;
56
+ }
57
+ /**
58
+ * Create a tsup configuration for building the standalone bundle.
59
+ *
60
+ * @param config - The canvas config.
61
+ * @param options - Additional options.
62
+ * @returns A tsup configuration object.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * // tsup.config.ts
67
+ * import { createTsupConfig, loadConfig } from 'drupal-canvas-react'
68
+ *
69
+ * export default loadConfig().then(createTsupConfig)
70
+ * ```
71
+ */
72
+ declare function createTsupConfig(config: CanvasConfig, options?: CreateTsupConfigOptions): Options;
73
+
74
+ export { CanvasConfig, createTsupConfig, defineConfig, generateComponentIndex, loadConfig, resolveConfigPath, writeComponentIndex };