@mayson-org/inject-script 1.0.1 → 1.0.4

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.
Files changed (55) hide show
  1. package/.env.example +9 -0
  2. package/README.md +66 -23
  3. package/bin/cli.mjs +1 -283
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +104 -0
  7. package/dist/core/find-root-layout.d.ts +2 -0
  8. package/dist/core/find-root-layout.d.ts.map +1 -0
  9. package/dist/core/find-root-layout.js +61 -0
  10. package/dist/core/generators.d.ts +4 -0
  11. package/dist/core/generators.d.ts.map +1 -0
  12. package/dist/core/generators.js +37 -0
  13. package/dist/core/injector.d.ts +4 -0
  14. package/dist/core/injector.d.ts.map +1 -0
  15. package/dist/core/injector.js +76 -0
  16. package/dist/core/types.d.ts +48 -0
  17. package/dist/core/types.d.ts.map +1 -0
  18. package/dist/core/types.js +1 -0
  19. package/dist/generators.d.ts.map +1 -1
  20. package/dist/generators.js +14 -27
  21. package/dist/index.d.ts +16 -7
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +58 -9
  24. package/dist/plugins/ascii/index.d.ts +4 -0
  25. package/dist/plugins/ascii/index.d.ts.map +1 -0
  26. package/dist/plugins/ascii/index.js +7 -0
  27. package/dist/plugins/index.d.ts +7 -0
  28. package/dist/plugins/index.d.ts.map +1 -0
  29. package/dist/plugins/index.js +9 -0
  30. package/dist/plugins/metadata/index.d.ts +3 -0
  31. package/dist/plugins/metadata/index.d.ts.map +1 -0
  32. package/dist/plugins/metadata/index.js +14 -0
  33. package/dist/plugins/watermark/index.d.ts +4 -0
  34. package/dist/plugins/watermark/index.d.ts.map +1 -0
  35. package/dist/plugins/watermark/index.js +7 -0
  36. package/dist/plugins/watermark/template.d.ts +9 -0
  37. package/dist/plugins/watermark/template.d.ts.map +1 -0
  38. package/dist/plugins/watermark/template.js +128 -0
  39. package/dist/shared/ascii-art.d.ts +10 -0
  40. package/dist/shared/ascii-art.d.ts.map +1 -0
  41. package/dist/shared/ascii-art.js +44 -0
  42. package/dist/shared/config.d.ts +13 -0
  43. package/dist/shared/config.d.ts.map +1 -0
  44. package/dist/shared/config.js +25 -0
  45. package/dist/shared/generated-app-metadata.d.ts +28 -0
  46. package/dist/shared/generated-app-metadata.d.ts.map +1 -0
  47. package/dist/shared/generated-app-metadata.js +103 -0
  48. package/dist/shared/remix-url.d.ts +13 -0
  49. package/dist/shared/remix-url.d.ts.map +1 -0
  50. package/dist/shared/remix-url.js +16 -0
  51. package/package.json +14 -9
  52. package/src/find-root-layout.ts +0 -75
  53. package/src/generators.ts +0 -176
  54. package/src/index.ts +0 -42
  55. package/src/injector.ts +0 -108
package/.env.example ADDED
@@ -0,0 +1,9 @@
1
+ # Set before running inject-script (e.g. in CI / Vercel build env)
2
+
3
+ MAYSON_WORKSPACE_ID=
4
+ MAYSON_COLLECTION_ID=
5
+ MAYSON_API_BASE_URL=
6
+ MAYSON_APP_BASE_URL=https://mayson.dev
7
+
8
+ # Auth for generated-app-metadata (show_remix_pill / enable_remix)
9
+ MAYSON_WEB_TOKEN=
package/README.md CHANGED
@@ -1,41 +1,84 @@
1
1
  # @mayson-org/inject-script
2
2
 
3
- A lightweight CLI tool and Node.js package designed for **Next.js App Router** applications. It dynamically locates the project's root layout file (e.g. `app/layout.tsx`, `src/app/layout.tsx`, or inside route groups like `app/(main)/layout.js`), generates standalone component files (`MaysonBadge.tsx`, `MaysonWatermark.tsx`, `MaysonAscii.tsx`, `MaysonMetadata.tsx`), and injects their imports and JSX tags into the layout tree.
3
+ Locate the Next.js App Router root layout, generate Mayson plugin components next to it, and inject imports + JSX into the layout. Injection logic is unchanged; plugins supply component source.
4
4
 
5
- ---
5
+ Requires **Node 20+**.
6
6
 
7
- ## Quick Usage via `npx`
8
-
9
- Run directly in any Next.js project root:
7
+ ## Install
10
8
 
11
9
  ```bash
12
- npx @mayson-org/inject-script
10
+ bun add -d @mayson-org/inject-script@latest
11
+ # or
12
+ npx @mayson-org/inject-script --type=watermark
13
13
  ```
14
14
 
15
- Or pass optional flags:
15
+ ## Quick usage
16
16
 
17
17
  ```bash
18
- # Specify component type (badge | watermark | ascii | metadata | all)
19
- npx @mayson-org/inject-script --type=badge
18
+ npx @mayson-org/inject-script --type=watermark
19
+ npx @mayson-org/inject-script --type=all
20
+ npx @mayson-org/inject-script --type=watermark --dry-run
21
+ npx @mayson-org/inject-script --dir=./my-app --type=watermark
22
+ ```
20
23
 
21
- # Dry-run mode (preview changes without modifying files)
22
- npx @mayson-org/inject-script --dry-run
24
+ ## Watermark
23
25
 
24
- # Specify target project directory
25
- npx @mayson-org/inject-script --dir=./my-nextjs-app
26
- ```
26
+ `--type=watermark` writes `MaysonWatermark.tsx` beside the root layout and injects `<MaysonWatermark />` before `</body>`.
27
+
28
+ The pill:
29
+
30
+ - Fixed bottom-right “Edit with Mayson” (logo + label)
31
+ - Dismiss (×) hides it for the session
32
+ - Click opens the Mayson remix URL (baked at inject time)
33
+
34
+ ### API gating (`show_remix_pill` / `enable_remix`)
35
+
36
+ At inject time the CLI fetches **generated-app-metadata** (same API as the old build plugins):
37
+
38
+ `GET {MAYSON_API_BASE_URL}/sigma/web/v1/generated-app-metadata/workspaces/{id}/collections/{id}`
39
+ Header: `M-Web-Token: {MAYSON_WEB_TOKEN}`
27
40
 
28
- ---
41
+ | API field | Effect |
42
+ |-----------|--------|
43
+ | `show_remix_pill` | If `false`, watermark is **not** injected; if `true`, inject |
44
+ | `enable_remix` | Baked into the component remix URL (`true` → collection remix redirect; otherwise app home) |
29
45
 
30
- ## Programmatic API Usage
46
+ **Metadata not found (404):** if the API returns HTTP 404 or a body like `{ "response_code": 404, "message": "Generated app metadata not found", "value": {} }`, treat as **`show_remix_pill=true`** and **`enable_remix=true`** (show watermark, remixable).
31
47
 
32
- ```javascript
33
- import { runAutoInject, findRootLayout, generateComponents, injectComponentsIntoLayout } from '@mayson-org/inject-script';
48
+ Defaults when the API is not called or fails for other reasons: `showRemixPill=false`, `enableRemix=false` (watermark skipped).
34
49
 
35
- // Auto-detect layout, generate 'badge' component file, and inject into layout
36
- const result = runAutoInject(process.cwd(), ['badge', 'watermark']);
50
+ ### Env vars (inject-time)
37
51
 
38
- if (result.success) {
39
- console.log('Injected components:', result.injectedComponents);
40
- }
52
+ | Variable | Purpose |
53
+ |----------|---------|
54
+ | `MAYSON_WORKSPACE_ID` | Workspace id |
55
+ | `MAYSON_COLLECTION_ID` | Collection id (also baked into remix URL) |
56
+ | `MAYSON_API_BASE_URL` | API base for metadata |
57
+ | `MAYSON_APP_BASE_URL` | App base (default `https://mayson.dev`) |
58
+ | `MAYSON_WEB_TOKEN` | Auth for generated-app-metadata |
59
+
60
+ ## Programmatic API
61
+
62
+ ```ts
63
+ import { runAutoInject } from '@mayson-org/inject-script';
64
+
65
+ const result = await runAutoInject(process.cwd()); // watermark + ascii by default
66
+ ```
67
+
68
+ ## Package layout
69
+
70
+ ```text
71
+ src/
72
+ cli.ts / index.ts
73
+ core/ # find-root-layout, injector, generators
74
+ plugins/watermark/ # remix pill template
75
+ plugins/ascii|metadata # stubs for later restores
76
+ shared/ # config, generated-app-metadata, remix-url
41
77
  ```
78
+
79
+ ## Notes
80
+
81
+ - Default inject is **watermark + ascii** (override with `--type`).
82
+ - Watermark is still gated by `show_remix_pill`; ascii always injects when selected.
83
+ - Idempotent: skips components already present in the layout.
84
+ - Does not patch `.next` HTML; source inject only.
package/bin/cli.mjs CHANGED
@@ -1,284 +1,2 @@
1
1
  #!/usr/bin/env node
2
-
3
- import fs from 'fs';
4
- import path from 'path';
5
-
6
- function findLayoutFiles(dir, fileList = []) {
7
- if (!fs.existsSync(dir)) return fileList;
8
- const entries = fs.readdirSync(dir, { withFileTypes: true });
9
-
10
- for (const entry of entries) {
11
- const fullPath = path.join(dir, entry.name);
12
- if (entry.isDirectory()) {
13
- if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;
14
- findLayoutFiles(fullPath, fileList);
15
- } else if (entry.isFile()) {
16
- const ext = path.extname(entry.name);
17
- const base = path.basename(entry.name, ext);
18
- if (base === 'layout' && ['.tsx', '.jsx', '.ts', '.js'].includes(ext)) {
19
- fileList.push(fullPath);
20
- }
21
- }
22
- }
23
- return fileList;
24
- }
25
-
26
- function scoreLayout(filePath, projectRoot) {
27
- const content = fs.readFileSync(filePath, 'utf-8');
28
- const relativePath = path.relative(projectRoot, filePath);
29
- const depth = relativePath.split(path.sep).length;
30
- let score = 100 - depth;
31
-
32
- if (/<html/i.test(content)) score += 50;
33
- if (/<body/i.test(content)) score += 50;
34
- if (/^src[/\\]app[/\\]layout\.[jt]sx?$/.test(relativePath) || /^app[/\\]layout\.[jt]sx?$/.test(relativePath)) {
35
- score += 40;
36
- }
37
- return score;
38
- }
39
-
40
- function findRootLayout(projectRoot) {
41
- const possibleDirs = [path.join(projectRoot, 'src', 'app'), path.join(projectRoot, 'app')];
42
- const candidates = [];
43
-
44
- for (const dir of possibleDirs) {
45
- if (fs.existsSync(dir)) findLayoutFiles(dir, candidates);
46
- }
47
-
48
- if (candidates.length === 0) return null;
49
- candidates.sort((a, b) => scoreLayout(b, projectRoot) - scoreLayout(a, projectRoot));
50
- return candidates[0];
51
- }
52
-
53
- const TEMPLATES = {
54
- badge: {
55
- name: 'MaysonBadge',
56
- code: `'use client';
57
- import React from 'react';
58
-
59
- export function MaysonBadge() {
60
- return (
61
- <div
62
- style={{
63
- position: 'fixed',
64
- bottom: '16px',
65
- right: '16px',
66
- zIndex: 99999,
67
- display: 'flex',
68
- alignItems: 'center',
69
- gap: '8px',
70
- padding: '6px 12px',
71
- backgroundColor: 'rgba(18, 18, 20, 0.9)',
72
- color: '#ffffff',
73
- borderRadius: '9999px',
74
- fontSize: '12px',
75
- fontWeight: 500,
76
- fontFamily: 'system-ui, -apple-system, sans-serif',
77
- border: '1px solid rgba(255, 255, 255, 0.15)',
78
- boxShadow: '0 4px 14px rgba(0, 0, 0, 0.25)',
79
- backdropFilter: 'blur(8px)',
80
- cursor: 'pointer',
81
- userSelect: 'none',
82
- }}
83
- >
84
- <span style={{ width: '8px', height: '8px', borderRadius: '50%', backgroundColor: '#6366f1' }} />
85
- <span>Built with <strong>Mayson</strong></span>
86
- </div>
87
- );
88
- }
89
- `,
90
- },
91
- watermark: {
92
- name: 'MaysonWatermark',
93
- code: `'use client';
94
- import React from 'react';
95
-
96
- export function MaysonWatermark() {
97
- return (
98
- <div
99
- style={{
100
- position: 'fixed',
101
- bottom: '12px',
102
- left: '12px',
103
- zIndex: 99999,
104
- opacity: 0.4,
105
- fontSize: '11px',
106
- fontFamily: 'monospace',
107
- color: '#888888',
108
- pointerEvents: 'none',
109
- }}
110
- >
111
- ⚡ Mayson Watermark
112
- </div>
113
- );
114
- }
115
- `,
116
- },
117
- ascii: {
118
- name: 'MaysonAscii',
119
- code: `'use client';
120
- import React from 'react';
121
-
122
- export function MaysonAscii() {
123
- return (
124
- <div
125
- style={{
126
- position: 'fixed',
127
- top: '8px',
128
- right: '12px',
129
- zIndex: 99999,
130
- fontSize: '10px',
131
- fontFamily: 'monospace',
132
- color: 'rgba(255, 255, 255, 0.6)',
133
- backgroundColor: 'rgba(0, 0, 0, 0.75)',
134
- padding: '4px 8px',
135
- borderRadius: '4px',
136
- pointerEvents: 'none',
137
- }}
138
- >
139
- MAYSON :: RUNTIME ACTIVE
140
- </div>
141
- );
142
- }
143
- `,
144
- },
145
- metadata: {
146
- name: 'MaysonMetadata',
147
- code: `'use client';
148
- import React from 'react';
149
-
150
- export function MaysonMetadata() {
151
- return <meta name="mayson-generator" content="Mayson App Platform" />;
152
- }
153
- `,
154
- },
155
- };
156
-
157
- function parseArgs() {
158
- const args = process.argv.slice(2);
159
- const options = {
160
- projectRoot: process.cwd(),
161
- type: 'badge',
162
- dryRun: false,
163
- help: false,
164
- };
165
-
166
- for (let i = 0; i < args.length; i++) {
167
- const arg = args[i];
168
- if (arg === '--help' || arg === '-h') {
169
- options.help = true;
170
- } else if (arg === '--dry-run') {
171
- options.dryRun = true;
172
- } else if (arg.startsWith('--type=')) {
173
- options.type = arg.split('=')[1];
174
- } else if (arg === '--type' && args[i + 1]) {
175
- options.type = args[++i];
176
- } else if (arg.startsWith('--dir=')) {
177
- options.projectRoot = path.resolve(arg.split('=')[1]);
178
- } else if (arg === '--dir' && args[i + 1]) {
179
- options.projectRoot = path.resolve(args[++i]);
180
- }
181
- }
182
- return options;
183
- }
184
-
185
- const options = parseArgs();
186
-
187
- if (options.help) {
188
- console.log(`
189
- @mayson-org/inject-script CLI - Generate & Inject Mayson Components into Next.js App Router
190
-
191
- Usage:
192
- npx @mayson-org/inject-script [options]
193
-
194
- Options:
195
- --dir <path> Target project root directory (default: current working directory)
196
- --type <type> Component type to inject: badge | watermark | ascii | metadata | all (default: badge)
197
- --dry-run Simulate component generation and layout injection without modifying files
198
- --help, -h Show this help message
199
- `);
200
- process.exit(0);
201
- }
202
-
203
- console.log(`🔍 Searching for Next.js root layout in: ${options.projectRoot}`);
204
-
205
- const layoutFile = findRootLayout(options.projectRoot);
206
-
207
- if (!layoutFile) {
208
- console.error(`❌ Error: Could not find any App Router layout file in ${options.projectRoot}`);
209
- process.exit(1);
210
- }
211
-
212
- console.log(`📍 Found Root Layout: ${path.relative(options.projectRoot, layoutFile)}`);
213
-
214
- const layoutDir = path.dirname(layoutFile);
215
- const isTypeScript = layoutFile.endsWith('.tsx') || layoutFile.endsWith('.ts');
216
- const ext = isTypeScript ? '.tsx' : '.jsx';
217
-
218
- const selectedTypes = options.type === 'all' ? ['badge', 'watermark', 'ascii', 'metadata'] : [options.type];
219
-
220
- let content = fs.readFileSync(layoutFile, 'utf-8');
221
- const injectedNames = [];
222
-
223
- for (const t of selectedTypes) {
224
- const template = TEMPLATES[t];
225
- if (!template) {
226
- console.warn(`⚠️ Unknown component type '${t}'. Skipping.`);
227
- continue;
228
- }
229
-
230
- const { name, code } = template;
231
- const compPath = path.join(layoutDir, `${name}${ext}`);
232
-
233
- // Create Component File
234
- if (options.dryRun) {
235
- console.log(`🧪 [Dry Run] Would create component file: ${path.relative(options.projectRoot, compPath)}`);
236
- } else {
237
- fs.writeFileSync(compPath, code, 'utf-8');
238
- console.log(`✨ Created Component File: ${path.relative(options.projectRoot, compPath)}`);
239
- }
240
-
241
- // Check if component already present in layout
242
- if (content.includes(name)) {
243
- console.log(`ℹ️ Component ${name} is already present in ${path.basename(layoutFile)}.`);
244
- continue;
245
- }
246
-
247
- // Insert import statement
248
- const importLine = `import { ${name} } from './${name}';\n`;
249
- if (/import\s+.*?from\s+['"].*?['"];?/g.test(content)) {
250
- const matches = Array.from(content.matchAll(/import\s+.*?from\s+['"].*?['"];?/g));
251
- const last = matches[matches.length - 1];
252
- const idx = last.index + last[0].length;
253
- content = content.slice(0, idx) + '\n' + importLine + content.slice(idx);
254
- } else {
255
- content = importLine + content;
256
- }
257
-
258
- // Insert JSX snippet
259
- const snippet = `\n {/* @mayson-component-injected: ${name} */}\n <${name} />`;
260
-
261
- if (/<\/body>/i.test(content)) {
262
- content = content.replace(/<\/body>/i, `${snippet}\n </body>`);
263
- } else if (/<\/html>/i.test(content)) {
264
- content = content.replace(/<\/html>/i, `${snippet}\n </html>`);
265
- } else {
266
- const lastIdx = content.lastIndexOf('</');
267
- if (lastIdx !== -1) {
268
- content = content.slice(0, lastIdx) + snippet + '\n ' + content.slice(lastIdx);
269
- }
270
- }
271
-
272
- injectedNames.push(name);
273
- }
274
-
275
- if (injectedNames.length > 0) {
276
- if (options.dryRun) {
277
- console.log(`🧪 [Dry Run] Would inject components into ${path.relative(options.projectRoot, layoutFile)}: ${injectedNames.join(', ')}`);
278
- } else {
279
- fs.writeFileSync(layoutFile, content, 'utf-8');
280
- console.log(`🎉 Injected components into ${path.relative(options.projectRoot, layoutFile)}: ${injectedNames.join(', ')}`);
281
- }
282
- } else {
283
- console.log(`✅ Root layout is up to date.`);
284
- }
2
+ import '../dist/cli.js';
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(argv?: string[]): Promise<void>;
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAwDA,wBAAsB,IAAI,CAAC,IAAI,WAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DtE"}
package/dist/cli.js ADDED
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ import path from 'node:path';
3
+ import { runAutoInject } from './index.js';
4
+ function printHelp() {
5
+ console.log(`
6
+ @mayson-org/inject-script — generate & inject Mayson components into Next.js App Router layout
7
+
8
+ Usage:
9
+ inject-script [options]
10
+ npx @mayson-org/inject-script [options]
11
+
12
+ Options:
13
+ --dir <path> Target project root (default: cwd)
14
+ --type <type> watermark | ascii | metadata | all (default: watermark + ascii)
15
+ --dry-run Preview without writing files
16
+ --help, -h Show help
17
+
18
+ Watermark gating (inject-time):
19
+ Fetches generated-app-metadata when MAYSON_WORKSPACE_ID, MAYSON_COLLECTION_ID,
20
+ MAYSON_API_BASE_URL, and MAYSON_WEB_TOKEN are set. Injects watermark only when
21
+ show_remix_pill is true; bakes enable_remix into the generated component.
22
+ `);
23
+ }
24
+ function parseArgs(argv) {
25
+ const options = {
26
+ projectRoot: process.cwd(),
27
+ types: ['watermark', 'ascii'],
28
+ dryRun: false,
29
+ help: false,
30
+ };
31
+ for (let i = 0; i < argv.length; i++) {
32
+ const arg = argv[i];
33
+ if (arg === '--help' || arg === '-h') {
34
+ options.help = true;
35
+ }
36
+ else if (arg === '--dry-run') {
37
+ options.dryRun = true;
38
+ }
39
+ else if (arg.startsWith('--type=')) {
40
+ options.types = [arg.split('=')[1]];
41
+ }
42
+ else if (arg === '--type' && argv[i + 1]) {
43
+ options.types = [argv[++i]];
44
+ }
45
+ else if (arg.startsWith('--dir=')) {
46
+ options.projectRoot = path.resolve(arg.split('=')[1]);
47
+ }
48
+ else if (arg === '--dir' && argv[i + 1]) {
49
+ options.projectRoot = path.resolve(argv[++i]);
50
+ }
51
+ else {
52
+ throw new Error(`unknown argument: ${arg}`);
53
+ }
54
+ }
55
+ return options;
56
+ }
57
+ export async function main(argv = process.argv.slice(2)) {
58
+ try {
59
+ const options = parseArgs(argv);
60
+ if (options.help) {
61
+ printHelp();
62
+ return;
63
+ }
64
+ console.log(`Searching for Next.js root layout in: ${options.projectRoot}`);
65
+ const result = await runAutoInject(options.projectRoot, options.types, options.dryRun);
66
+ if (!result.success) {
67
+ console.error(`Error: ${result.error}`);
68
+ process.exitCode = 1;
69
+ return;
70
+ }
71
+ const relativeLayout = path.relative(options.projectRoot, result.filePath);
72
+ console.log(`Found root layout: ${relativeLayout}`);
73
+ for (const message of result.messages ?? []) {
74
+ console.log(`[mayson] ${message}`);
75
+ }
76
+ if (options.dryRun) {
77
+ console.log(`[dry-run] Would inject: ${result.injectedComponents.join(', ') || '(none)'}`);
78
+ if (result.skippedComponents.length > 0) {
79
+ console.log(`[dry-run] Already present: ${result.skippedComponents.join(', ')}`);
80
+ }
81
+ return;
82
+ }
83
+ if (result.injectedComponents.length > 0) {
84
+ console.log(`Injected: ${result.injectedComponents.join(', ')}`);
85
+ }
86
+ if (result.skippedComponents.length > 0) {
87
+ console.log(`Skipped (already present): ${result.skippedComponents.join(', ')}`);
88
+ }
89
+ if (result.injectedComponents.length === 0 &&
90
+ result.skippedComponents.length === 0 &&
91
+ (result.messages?.length ?? 0) > 0) {
92
+ console.log('Nothing to inject.');
93
+ }
94
+ else if (result.injectedComponents.length === 0 &&
95
+ result.skippedComponents.length > 0) {
96
+ console.log('Root layout is up to date.');
97
+ }
98
+ }
99
+ catch (err) {
100
+ console.error(`Error: ${err.message}`);
101
+ process.exitCode = 1;
102
+ }
103
+ }
104
+ void main();
@@ -0,0 +1,2 @@
1
+ export declare function findRootLayout(projectRoot?: string): string | null;
2
+ //# sourceMappingURL=find-root-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-root-layout.d.ts","sourceRoot":"","sources":["../../src/core/find-root-layout.ts"],"names":[],"mappings":"AAgDA,wBAAgB,cAAc,CAAC,WAAW,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CA0BjF"}
@@ -0,0 +1,61 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ const LAYOUT_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js'];
4
+ function findLayoutFiles(dir, fileList = []) {
5
+ if (!fs.existsSync(dir))
6
+ return fileList;
7
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
8
+ for (const entry of entries) {
9
+ const fullPath = path.join(dir, entry.name);
10
+ if (entry.isDirectory()) {
11
+ if (entry.name === 'node_modules' || entry.name.startsWith('.')) {
12
+ continue;
13
+ }
14
+ findLayoutFiles(fullPath, fileList);
15
+ }
16
+ else if (entry.isFile()) {
17
+ const ext = path.extname(entry.name);
18
+ const base = path.basename(entry.name, ext);
19
+ if (base === 'layout' && LAYOUT_EXTENSIONS.includes(ext)) {
20
+ fileList.push(fullPath);
21
+ }
22
+ }
23
+ }
24
+ return fileList;
25
+ }
26
+ function scoreLayoutCandidate(filePath, projectRoot) {
27
+ const content = fs.readFileSync(filePath, 'utf-8');
28
+ const relativePath = path.relative(projectRoot, filePath);
29
+ const depth = relativePath.split(path.sep).length;
30
+ let score = 100 - depth;
31
+ if (/<html/i.test(content))
32
+ score += 50;
33
+ if (/<body/i.test(content))
34
+ score += 50;
35
+ const isDirectAppChild = /^src[/\\]app[/\\]layout\.[jt]sx?$/.test(relativePath) ||
36
+ /^app[/\\]layout\.[jt]sx?$/.test(relativePath);
37
+ if (isDirectAppChild)
38
+ score += 40;
39
+ return score;
40
+ }
41
+ export function findRootLayout(projectRoot = process.cwd()) {
42
+ const possibleAppDirs = [
43
+ path.join(projectRoot, 'src', 'app'),
44
+ path.join(projectRoot, 'app'),
45
+ ];
46
+ const candidateFiles = [];
47
+ for (const appDir of possibleAppDirs) {
48
+ if (fs.existsSync(appDir)) {
49
+ findLayoutFiles(appDir, candidateFiles);
50
+ }
51
+ }
52
+ if (candidateFiles.length === 0) {
53
+ return null;
54
+ }
55
+ const scored = candidateFiles.map((filePath) => ({
56
+ filePath,
57
+ score: scoreLayoutCandidate(filePath, projectRoot),
58
+ }));
59
+ scored.sort((a, b) => b.score - a.score);
60
+ return scored[0]?.filePath || null;
61
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentGeneratorOptions, ComponentType, GeneratedComponentInfo } from './types.js';
2
+ export type { ComponentGeneratorOptions, ComponentType, GeneratedComponentInfo, } from './types.js';
3
+ export declare function generateComponents(types: ComponentType[] | undefined, options: ComponentGeneratorOptions): GeneratedComponentInfo[];
4
+ //# sourceMappingURL=generators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/core/generators.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,yBAAyB,EACzB,aAAa,EACb,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,yBAAyB,EACzB,aAAa,EACb,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAQpB,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,aAAa,EAAE,YAAyB,EAC/C,OAAO,EAAE,yBAAyB,GACjC,sBAAsB,EAAE,CAkC1B"}
@@ -0,0 +1,37 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { plugins } from '../plugins/index.js';
4
+ const ALL_TYPES = [
5
+ 'watermark',
6
+ 'ascii',
7
+ 'metadata',
8
+ ];
9
+ export function generateComponents(types = ['watermark', 'ascii'], options) {
10
+ const { outputDir, isTypeScript = true, dryRun = false } = options;
11
+ const ext = isTypeScript ? '.tsx' : '.jsx';
12
+ const results = [];
13
+ if (!dryRun && !fs.existsSync(outputDir)) {
14
+ fs.mkdirSync(outputDir, { recursive: true });
15
+ }
16
+ const selectedTypes = types.includes('all')
17
+ ? ALL_TYPES
18
+ : types.filter((t) => t !== 'all');
19
+ for (const type of selectedTypes) {
20
+ const plugin = plugins[type];
21
+ if (!plugin) {
22
+ continue;
23
+ }
24
+ const code = plugin.generateSource(options.context);
25
+ const filePath = path.join(outputDir, `${plugin.name}${ext}`);
26
+ if (!dryRun) {
27
+ fs.writeFileSync(filePath, code, 'utf-8');
28
+ }
29
+ results.push({
30
+ name: plugin.name,
31
+ filePath,
32
+ importStatement: `import { ${plugin.name} } from './${plugin.name}';`,
33
+ jsxTag: `<${plugin.name} />`,
34
+ });
35
+ }
36
+ return results;
37
+ }
@@ -0,0 +1,4 @@
1
+ import type { InjectorOptions, InjectorResult } from './types.js';
2
+ export type { InjectorOptions, InjectorResult } from './types.js';
3
+ export declare function injectComponentsIntoLayout(options: InjectorOptions): InjectorResult;
4
+ //# sourceMappingURL=injector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"injector.d.ts","sourceRoot":"","sources":["../../src/core/injector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAElE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAElE,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc,CAqFnF"}