coffeehaml 0.6.0 → 0.7.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.
Files changed (66) hide show
  1. package/README.md +21 -5
  2. package/dist/ast.js +334 -123
  3. package/dist/cli.js +363 -291
  4. package/dist/compiler.js +126 -89
  5. package/dist/emitter.js +771 -736
  6. package/dist/errors.js +5 -2
  7. package/dist/expressions.js +211 -154
  8. package/dist/index.js +22 -4
  9. package/dist/lexer.js +536 -340
  10. package/dist/parser.js +533 -432
  11. package/dist/prettier/coffeescript-formatter.js +309 -0
  12. package/dist/prettier/index.js +105 -0
  13. package/dist/prettier/options.js +232 -0
  14. package/dist/prettier/printer.js +559 -0
  15. package/dist/src/ast.js +338 -0
  16. package/dist/src/cli.js +430 -0
  17. package/dist/src/compiler.js +132 -0
  18. package/dist/src/emitter.js +791 -0
  19. package/dist/src/errors.js +5 -0
  20. package/dist/src/expressions.js +223 -0
  21. package/dist/src/index.js +22 -0
  22. package/dist/src/lexer.js +504 -0
  23. package/dist/src/parser.js +575 -0
  24. package/dist/src/types.js +122 -0
  25. package/dist/src/vite-plugin.js +78 -0
  26. package/dist/types.js +122 -12
  27. package/dist/vite-plugin.js +78 -63
  28. package/docs/examples.md +201 -1
  29. package/docs/grammar.md +90 -1
  30. package/docs/index.html +260 -37
  31. package/package.json +12 -10
  32. package/syntaxes/CoffeeHaml.tmLanguage +815 -868
  33. package/syntaxes/coffeehaml.tmLanguage.json +352 -161
  34. package/dist/ast.d.ts +0 -117
  35. package/dist/ast.d.ts.map +0 -1
  36. package/dist/ast.js.map +0 -1
  37. package/dist/cli.d.ts +0 -3
  38. package/dist/cli.d.ts.map +0 -1
  39. package/dist/cli.js.map +0 -1
  40. package/dist/compiler.d.ts +0 -10
  41. package/dist/compiler.d.ts.map +0 -1
  42. package/dist/compiler.js.map +0 -1
  43. package/dist/emitter.d.ts +0 -4
  44. package/dist/emitter.d.ts.map +0 -1
  45. package/dist/emitter.js.map +0 -1
  46. package/dist/errors.d.ts +0 -3
  47. package/dist/errors.d.ts.map +0 -1
  48. package/dist/errors.js.map +0 -1
  49. package/dist/expressions.d.ts +0 -22
  50. package/dist/expressions.d.ts.map +0 -1
  51. package/dist/expressions.js.map +0 -1
  52. package/dist/index.d.ts +0 -5
  53. package/dist/index.d.ts.map +0 -1
  54. package/dist/index.js.map +0 -1
  55. package/dist/lexer.d.ts +0 -30
  56. package/dist/lexer.d.ts.map +0 -1
  57. package/dist/lexer.js.map +0 -1
  58. package/dist/parser.d.ts +0 -10
  59. package/dist/parser.d.ts.map +0 -1
  60. package/dist/parser.js.map +0 -1
  61. package/dist/types.d.ts +0 -76
  62. package/dist/types.d.ts.map +0 -1
  63. package/dist/types.js.map +0 -1
  64. package/dist/vite-plugin.d.ts +0 -7
  65. package/dist/vite-plugin.d.ts.map +0 -1
  66. package/dist/vite-plugin.js.map +0 -1
package/dist/cli.js CHANGED
@@ -1,22 +1,59 @@
1
- #!/usr/bin/env node
2
- import { compileFile } from './index.js';
3
- import { writeFileSync, readFileSync, existsSync, watch, statSync, readdirSync } from 'fs';
4
- import { fileURLToPath } from 'url';
5
- import { dirname, join, resolve, relative } from 'path';
6
- import { createInterface } from 'readline';
7
- function getVersion() {
8
- try {
9
- const __dirname = dirname(fileURLToPath(import.meta.url));
10
- const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
11
- return pkg.version || '0.0.0';
12
- }
13
- catch {
14
- return '0.0.0';
15
- }
16
- }
17
- const args = process.argv.slice(2);
18
- if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
19
- console.log(`CoffeeHaml v${getVersion()}
1
+ // Generated by CoffeeScript 2.7.0
2
+ //!/usr/bin/env node
3
+ // cli.coffee — CoffeeHaml command-line interface
4
+ var _require, args, ask, cmd, compileFn, doCompile, doInit, file, files, findFiles, formatError, formatWarning, getVersion, i, initial, len, ok, opts, parseArgs, parseWrapFlag, rest, stats, target, timers, watchDir, watcher,
5
+ indexOf = [].indexOf;
6
+
7
+ import {
8
+ compileFile
9
+ } from './index.js';
10
+
11
+ import {
12
+ writeFileSync,
13
+ readFileSync,
14
+ existsSync,
15
+ watch,
16
+ statSync,
17
+ readdirSync,
18
+ mkdirSync
19
+ } from 'fs';
20
+
21
+ import {
22
+ fileURLToPath
23
+ } from 'url';
24
+
25
+ import {
26
+ dirname,
27
+ join,
28
+ resolve,
29
+ relative
30
+ } from 'path';
31
+
32
+ import {
33
+ createInterface
34
+ } from 'readline';
35
+
36
+ import {
37
+ createRequire
38
+ } from 'module';
39
+
40
+ _require = createRequire(import.meta.url);
41
+
42
+ getVersion = function() {
43
+ var __dirname, pkg;
44
+ try {
45
+ __dirname = dirname(fileURLToPath(import.meta.url));
46
+ pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
47
+ return pkg.version || '0.0.0';
48
+ } catch (error) {
49
+ return '0.0.0';
50
+ }
51
+ };
52
+
53
+ args = process.argv.slice(2);
54
+
55
+ if (args.length === 0 || indexOf.call(args, '--help') >= 0 || indexOf.call(args, '-h') >= 0) {
56
+ console.log(`CoffeeHaml v${getVersion()}
20
57
  Haml structure. CoffeeScript semantics. React runtime.
21
58
 
22
59
  Usage:
@@ -31,178 +68,193 @@ Options:
31
68
  -h, --help Show this help
32
69
 
33
70
  Examples:
34
- coffeehaml init # interactive project scaffold
71
+ coffeehaml init
35
72
  coffeehaml compile app.chaml -o app.js --wrap component
36
73
  coffeehaml compile app.chaml # prints to stdout
37
- coffeehaml watch src/ --wrap observer # watch and recompile on change
38
- `);
39
- process.exit(0);
74
+ coffeehaml watch src/ --wrap observer # watch and recompile on change`);
75
+ process.exit(0);
40
76
  }
41
- function parseWrapFlag(args) {
42
- const idx = args.indexOf('--wrap');
43
- if (idx === -1)
44
- return undefined;
45
- const val = args[idx + 1];
46
- if (!val || val.startsWith('-'))
47
- return undefined;
48
- // 'component' and 'observer' are shorthands; comma-separated → string[]
49
- if (val === 'component' || val === 'observer')
50
- return val;
51
- if (val === 'none')
52
- return 'none';
53
- return val.split(',').map(s => s.trim()).filter(Boolean);
54
- }
55
- function parseArgs(args) {
56
- const outIdx = args.indexOf('-o') !== -1 ? args.indexOf('-o') : args.indexOf('--output');
57
- const output = outIdx !== -1 ? args[outIdx + 1] : undefined;
58
- const sourceMap = args.includes('--source-map');
59
- const wrap = parseWrapFlag(args);
60
- return { input: args[0], output, sourceMap, wrap };
61
- }
62
- function formatError(err, filename) {
63
- const loc = err.location;
64
- let msg = loc
65
- ? `${filename}:${loc.start.line + 1}:${loc.start.column + 1}: [${err.code}] ${err.message}`
66
- : `[${err.code}] ${err.message}`;
67
- if (err.hint)
68
- msg += `\n Hint: ${err.hint}`;
69
- return msg;
70
- }
71
- function formatWarning(warn, filename) {
72
- const loc = warn.location;
73
- if (loc) {
74
- return `Warning: ${filename}:${loc.start.line + 1}:${loc.start.column + 1}: ${warn.message}`;
75
- }
77
+
78
+ parseWrapFlag = function(args) {
79
+ var idx, val;
80
+ idx = args.indexOf('--wrap');
81
+ if (idx === -1) {
82
+ return void 0;
83
+ }
84
+ val = args[idx + 1];
85
+ if (!(val && !val.startsWith('-'))) {
86
+ return void 0;
87
+ }
88
+ if (val === 'component' || val === 'observer' || val === 'none') {
89
+ return val;
90
+ }
91
+ return val.split(',').map(function(s) {
92
+ return s.trim();
93
+ }).filter(Boolean);
94
+ };
95
+
96
+ parseArgs = function(args) {
97
+ var outIdx, output, sourceMap, wrap;
98
+ outIdx = args.indexOf('-o') !== -1 ? args.indexOf('-o') : args.indexOf('--output');
99
+ output = outIdx !== -1 ? args[outIdx + 1] : void 0;
100
+ sourceMap = indexOf.call(args, '--source-map') >= 0;
101
+ wrap = parseWrapFlag(args);
102
+ return {
103
+ input: args[0],
104
+ output,
105
+ sourceMap,
106
+ wrap
107
+ };
108
+ };
109
+
110
+ formatError = function(err, filename) {
111
+ var loc, msg;
112
+ loc = err.location;
113
+ msg = loc ? `${filename}:${loc.start.line + 1}:${loc.start.column + 1}: [${err.code}] ${err.message}` : `[${err.code}] ${err.message}`;
114
+ if (err.hint) {
115
+ msg += `\n Hint: ${err.hint}`;
116
+ }
117
+ return msg;
118
+ };
119
+
120
+ formatWarning = function(warn, filename) {
121
+ var loc;
122
+ loc = warn.location;
123
+ if (loc) {
124
+ return `Warning: ${filename}:${loc.start.line + 1}:${loc.start.column + 1}: ${warn.message}`;
125
+ } else {
76
126
  return `Warning: ${warn.message}`;
77
- }
78
- function doCompile(input, opts) {
79
- const result = compileFile(input, { filename: input, sourceMap: opts.sourceMap, wrap: opts.wrap });
80
- if (result.errors.length > 0) {
81
- for (const err of result.errors) {
82
- console.error(formatError(err, input));
83
- }
84
- return false;
85
- }
86
- for (const warn of result.warnings) {
87
- console.error(formatWarning(warn, input));
88
- }
89
- if (opts.output) {
90
- writeFileSync(opts.output, result.code);
91
- if (result.sourceMap) {
92
- writeFileSync(opts.output + '.map', result.sourceMap);
93
- }
94
- console.error(`Compiled: ${input} → ${opts.output}`);
127
+ }
128
+ };
129
+
130
+ doCompile = function(input, opts) {
131
+ var err, i, j, len, len1, ref, ref1, result, warn;
132
+ result = compileFile(input, {
133
+ filename: input,
134
+ sourceMap: opts.sourceMap,
135
+ wrap: opts.wrap
136
+ });
137
+ if (result.errors.length > 0) {
138
+ ref = result.errors;
139
+ for (i = 0, len = ref.length; i < len; i++) {
140
+ err = ref[i];
141
+ console.error(formatError(err, input));
95
142
  }
96
- else {
97
- process.stdout.write(result.code);
143
+ return false;
144
+ }
145
+ ref1 = result.warnings;
146
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
147
+ warn = ref1[j];
148
+ console.error(formatWarning(warn, input));
149
+ }
150
+ if (opts.output) {
151
+ writeFileSync(opts.output, result.code);
152
+ if (result.sourceMap) {
153
+ writeFileSync(opts.output + '.map', result.sourceMap);
98
154
  }
99
- return true;
100
- }
155
+ console.error(`Compiled: ${input} → ${opts.output}`);
156
+ } else {
157
+ process.stdout.write(result.code);
158
+ }
159
+ return true;
160
+ };
161
+
101
162
  // ─── Init ──────────────────────────────────────────────────
102
- function ask(q) {
103
- const rl = createInterface({ input: process.stdin, output: process.stdout });
104
- return new Promise(resolve => {
105
- rl.question(q, (answer) => {
106
- rl.close();
107
- resolve(answer.trim());
108
- });
163
+ ask = function(q) {
164
+ var rl;
165
+ rl = createInterface({
166
+ input: process.stdin,
167
+ output: process.stdout
168
+ });
169
+ return new Promise(function(resolve) {
170
+ return rl.question(q, function(answer) {
171
+ rl.close();
172
+ return resolve(answer.trim());
109
173
  });
110
- }
111
- async function doInit() {
112
- const cwd = process.cwd();
113
- const hasPkg = existsSync(join(cwd, 'package.json'));
114
- const hasViteConfig = existsSync(join(cwd, 'vite.config.ts')) || existsSync(join(cwd, 'vite.config.js'));
115
- const hasTsConfig = existsSync(join(cwd, 'tsconfig.json'));
116
- console.log(`\n ☕ CoffeeHaml init — project scaffold\n`);
117
- console.log(` Working directory: ${cwd}`);
118
- console.log(` Detected: ${[
119
- hasPkg ? 'package.json' : '',
120
- hasViteConfig ? 'Vite' : '',
121
- hasTsConfig ? 'TypeScript' : '',
122
- !hasPkg ? 'bare project' : '',
123
- ].filter(Boolean).join(', ')}\n`);
124
- // Question 1: Add dependency?
125
- let addDep = true;
126
- if (hasPkg) {
127
- const ans = await ask(' Add coffeehaml as devDependency? (Y/n): ');
128
- addDep = !/^n/i.test(ans);
129
- }
130
- else {
131
- console.log(' No package.json found — skipping dependency install.');
132
- addDep = false;
174
+ });
175
+ };
176
+
177
+ doInit = async function() {
178
+ var addDep, addScripts, ans, configPath, content, createSample, cwd, detections, execSync, existing, ext, hasImport, hasPkg, hasPlugin, hasTsConfig, hasViteConfig, pkg, pkgPath, sample, samplePath, setupVite, srcDir;
179
+ cwd = process.cwd();
180
+ hasPkg = existsSync(join(cwd, 'package.json'));
181
+ hasViteConfig = existsSync(join(cwd, 'vite.config.ts')) || existsSync(join(cwd, 'vite.config.js'));
182
+ hasTsConfig = existsSync(join(cwd, 'tsconfig.json'));
183
+ console.log("\n ☕ CoffeeHaml init — project scaffold\n");
184
+ console.log(` Working directory: ${cwd}`);
185
+ detections = [];
186
+ if (hasPkg) {
187
+ detections.push('package.json');
188
+ }
189
+ if (hasViteConfig) {
190
+ detections.push('Vite');
191
+ }
192
+ if (hasTsConfig) {
193
+ detections.push('TypeScript');
194
+ }
195
+ if (!hasPkg) {
196
+ detections.push('bare project');
197
+ }
198
+ console.log(` Detected: ${detections.join(', ')}\n`);
199
+ addDep = true;
200
+ if (hasPkg) {
201
+ ans = (await ask(' Add coffeehaml as devDependency? (Y/n): '));
202
+ addDep = !/^n/i.test(ans);
203
+ } else {
204
+ console.log(' No package.json found — skipping dependency install.');
205
+ addDep = false;
206
+ }
207
+ setupVite = false;
208
+ if (hasViteConfig) {
209
+ ans = (await ask(' Configure Vite plugin? (Y/n): '));
210
+ setupVite = !/^n/i.test(ans);
211
+ } else if (hasPkg) {
212
+ ans = (await ask(' No vite.config detected. Create one with CoffeeHaml plugin? (y/N): '));
213
+ setupVite = /^y/i.test(ans);
214
+ }
215
+ createSample = true;
216
+ ans = (await ask(' Create sample .chaml component? (Y/n): '));
217
+ createSample = !/^n/i.test(ans);
218
+ addScripts = false;
219
+ if (hasPkg) {
220
+ ans = (await ask(' Add build/watch scripts to package.json? (Y/n): '));
221
+ addScripts = !/^n/i.test(ans);
222
+ }
223
+ console.log('');
224
+ // Execute
225
+ if (addDep) {
226
+ console.log(' ⚡ Installing coffeehaml...');
227
+ ({execSync} = _require('child_process'));
228
+ try {
229
+ execSync('npm install --save-dev coffeehaml', {
230
+ cwd: cwd,
231
+ stdio: 'inherit'
232
+ });
233
+ console.log(' ✓ coffeehaml installed\n');
234
+ } catch (error) {
235
+ console.log(' ✗ Install failed — continuing anyway\n');
133
236
  }
134
- // Question 2: Vite plugin?
135
- let setupVite = false;
237
+ }
238
+ if (setupVite) {
239
+ configPath = hasViteConfig ? existsSync(join(cwd, 'vite.config.ts')) ? join(cwd, 'vite.config.ts') : join(cwd, 'vite.config.js') : join(cwd, 'vite.config.ts');
240
+ ext = configPath.endsWith('.ts') ? 'ts' : 'js';
136
241
  if (hasViteConfig) {
137
- const ans = await ask(' Configure Vite plugin? (Y/n): ');
138
- setupVite = !/^n/i.test(ans);
139
- }
140
- else if (hasPkg) {
141
- const ans = await ask(' No vite.config detected. Create one with CoffeeHaml plugin? (y/N): ');
142
- setupVite = /^y/i.test(ans);
143
- }
144
- // Question 3: Sample component?
145
- let createSample = true;
146
- const ans = await ask(' Create sample .chaml component? (Y/n): ');
147
- createSample = !/^n/i.test(ans);
148
- // Question 4: npm scripts?
149
- let addScripts = false;
150
- if (hasPkg) {
151
- const ans = await ask(' Add build/watch scripts to package.json? (Y/n): ');
152
- addScripts = !/^n/i.test(ans);
153
- }
154
- console.log();
155
- // ── Execute ──
156
- // 1. Install dependency
157
- if (addDep) {
158
- console.log(' ⚡ Installing coffeehaml...');
159
- const { execSync } = await import('child_process');
160
- try {
161
- execSync('npm install --save-dev coffeehaml', { cwd, stdio: 'inherit' });
162
- console.log(' ✓ coffeehaml installed\n');
242
+ existing = readFileSync(configPath, 'utf-8');
243
+ hasImport = /coffeehaml/.test(existing);
244
+ hasPlugin = /coffeehaml\(\)/.test(existing);
245
+ if (!hasImport) {
246
+ existing = "import coffeehaml from 'coffeehaml/vite-plugin';\n" + existing;
247
+ }
248
+ if (!hasPlugin) {
249
+ existing = existing.replace(/(plugins\s*:\s*\[)/, '$1\n coffeehaml(),');
250
+ if (!/plugins\s*:\s*\[/.test(existing)) {
251
+ existing = existing.replace(/(defineConfig\s*\(\s*\{)/, '$1\n plugins: [coffeehaml()],');
163
252
  }
164
- catch {
165
- console.log(' ✗ Install failed — continuing anyway\n');
166
- }
167
- }
168
- // 2. Vite plugin
169
- if (setupVite) {
170
- const configPath = join(cwd, hasViteConfig
171
- ? (existsSync(join(cwd, 'vite.config.ts')) ? 'vite.config.ts' : 'vite.config.js')
172
- : 'vite.config.ts');
173
- const ext = configPath.endsWith('.ts') ? 'ts' : 'js';
174
- if (hasViteConfig) {
175
- // Modify existing vite config — inject the import and plugin
176
- let existing = readFileSync(configPath, 'utf-8');
177
- const hasImport = /coffeehaml/.test(existing);
178
- const hasPlugin = /coffeehaml\(\)/.test(existing);
179
- if (!hasImport) {
180
- existing = `import coffeehaml from 'coffeehaml/vite-plugin';\n` + existing;
181
- }
182
- if (!hasPlugin) {
183
- // Insert coffeehaml() into plugins array
184
- existing = existing.replace(/(plugins\s*:\s*\[)/, '$1\n coffeehaml(),');
185
- if (!/plugins\s*:\s*\[/.test(existing)) {
186
- // No plugins array — add one after defineConfig({
187
- existing = existing.replace(/(defineConfig\s*\(\s*\{)/, '$1\n plugins: [coffeehaml()],');
188
- }
189
- }
190
- writeFileSync(configPath, existing);
191
- console.log(` ✓ Updated ${relative(cwd, configPath)}\n`);
192
- }
193
- else {
194
- // Create new vite config
195
- const tsContent = `import { defineConfig } from 'vite';
196
- import react from '@vitejs/plugin-react';
197
- import coffeehaml from 'coffeehaml/vite-plugin';
198
-
199
- export default defineConfig({
200
- plugins: [
201
- react(),
202
- coffeehaml(),
203
- ],
204
- });`;
205
- const jsContent = `import { defineConfig } from 'vite';
253
+ }
254
+ writeFileSync(configPath, existing);
255
+ console.log(` ✓ Updated ${relative(cwd, configPath)}\n`);
256
+ } else {
257
+ content = `import { defineConfig } from 'vite';
206
258
  import react from '@vitejs/plugin-react';
207
259
  import coffeehaml from 'coffeehaml/vite-plugin';
208
260
 
@@ -212,23 +264,22 @@ export default defineConfig({
212
264
  coffeehaml(),
213
265
  ],
214
266
  });`;
215
- writeFileSync(configPath, ext === 'ts' ? tsContent : jsContent);
216
- console.log(` ✓ Created ${relative(cwd, configPath)}\n`);
217
- }
267
+ writeFileSync(configPath, content);
268
+ console.log(` ✓ Created ${relative(cwd, configPath)}\n`);
218
269
  }
219
- // 3. Sample component
220
- if (createSample) {
221
- const srcDir = join(cwd, 'src');
222
- if (!existsSync(srcDir)) {
223
- const { mkdirSync } = await import('fs');
224
- mkdirSync(srcDir, { recursive: true });
225
- }
226
- const samplePath = join(srcDir, 'App.chaml');
227
- if (existsSync(samplePath)) {
228
- console.log(` ⚠ ${relative(cwd, samplePath)} already exists — skipping\n`);
229
- }
230
- else {
231
- const sample = `-# App.chaml — a sample CoffeeHaml component
270
+ }
271
+ if (createSample) {
272
+ srcDir = join(cwd, 'src');
273
+ if (!existsSync(srcDir)) {
274
+ mkdirSync(srcDir, {
275
+ recursive: true
276
+ });
277
+ }
278
+ samplePath = join(srcDir, 'App.chaml');
279
+ if (existsSync(samplePath)) {
280
+ console.log(` ⚠ ${relative(cwd, samplePath)} already exists — skipping\n`);
281
+ } else {
282
+ sample = `-# App.chaml — a sample CoffeeHaml component
232
283
  import { useState } from 'react'
233
284
 
234
285
  %div.app
@@ -239,120 +290,141 @@ import { useState } from 'react'
239
290
  %main
240
291
  - name = 'World'
241
292
  %section.card
242
- %h2= "Hello, #{name}!"
293
+ %h2= "Hello, \#{name}!"
243
294
  %p= "Counter: 0"
244
295
 
245
296
  %footer
246
- %small Built with coffeehaml v${getVersion()}
247
- `;
248
- writeFileSync(samplePath, sample);
249
- console.log(` ✓ Created ${relative(cwd, samplePath)}\n`);
250
- }
297
+ %small Built with coffeehaml v${getVersion()}`;
298
+ writeFileSync(samplePath, sample);
299
+ console.log(` ✓ Created ${relative(cwd, samplePath)}\n`);
251
300
  }
252
- // 4. npm scripts
253
- if (addScripts && hasPkg) {
254
- const pkgPath = join(cwd, 'package.json');
255
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
256
- pkg.scripts = pkg.scripts || {};
257
- if (!pkg.scripts['build:chaml']) {
258
- pkg.scripts['build:chaml'] = 'coffeehaml compile src/ -o dist/';
259
- }
260
- if (!pkg.scripts['watch:chaml']) {
261
- pkg.scripts['watch:chaml'] = 'coffeehaml watch src/ --wrap component';
262
- }
263
- writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
264
- console.log(' ✓ Added build:chaml and watch:chaml scripts\n');
301
+ }
302
+ if (addScripts && hasPkg) {
303
+ pkgPath = join(cwd, 'package.json');
304
+ pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
305
+ pkg.scripts = pkg.scripts || {};
306
+ if (!pkg.scripts['build:chaml']) {
307
+ pkg.scripts['build:chaml'] = 'coffeehaml compile src/ -o dist/';
265
308
  }
266
- console.log(' 🎉 Done! Start building:\n');
267
- console.log(' npm run build:chaml # compile once');
268
- console.log(' npm run watch:chaml # watch and recompile');
269
- console.log(' npx coffeehaml watch src/ # ad-hoc watch\n');
270
- }
271
- const cmd = args[0];
272
- const rest = args.slice(1);
273
- if (cmd === 'compile') {
274
- const opts = parseArgs(rest);
309
+ if (!pkg.scripts['watch:chaml']) {
310
+ pkg.scripts['watch:chaml'] = 'coffeehaml watch src/ --wrap component';
311
+ }
312
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
313
+ console.log(' ✓ Added build:chaml and watch:chaml scripts\n');
314
+ }
315
+ console.log(' 🎉 Done! Start building:\n');
316
+ console.log(' npm run build:chaml # compile once');
317
+ console.log(' npm run watch:chaml # watch and recompile');
318
+ return console.log(' npx coffeehaml watch src/ # ad-hoc watch\n');
319
+ };
320
+
321
+ // ─── Main ──────────────────────────────────────────────────
322
+ cmd = args[0];
323
+
324
+ rest = args.slice(1);
325
+
326
+ switch (cmd) {
327
+ case 'compile':
328
+ opts = parseArgs(rest);
275
329
  if (!opts.input) {
276
- console.error('Error: no input file specified');
277
- process.exit(1);
330
+ console.error('Error: no input file specified');
331
+ process.exit(1);
278
332
  }
279
- const ok = doCompile(opts.input, opts);
333
+ ok = doCompile(opts.input, opts);
280
334
  process.exit(ok ? 0 : 1);
281
- }
282
- else if (cmd === 'watch') {
283
- const opts = parseArgs(rest);
335
+ break;
336
+ case 'watch':
337
+ opts = parseArgs(rest);
284
338
  if (!opts.input) {
285
- console.error('Error: no input path specified');
286
- process.exit(1);
339
+ console.error('Error: no input path specified');
340
+ process.exit(1);
287
341
  }
288
- const target = resolve(opts.input);
289
- const stats = existsSync(target) ? statSync(target) : null;
290
- // Collect .chaml/.coffeehaml files
291
- const files = [];
292
- function findFiles(dir) {
293
- if (!existsSync(dir))
294
- return;
295
- const stat = statSync(dir);
296
- if (stat.isDirectory()) {
297
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
298
- const full = join(dir, entry.name);
299
- if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
300
- findFiles(full);
301
- }
302
- else if (entry.isFile() && /\.(chaml|coffeehaml)$/.test(entry.name)) {
303
- files.push(full);
304
- }
305
- }
306
- }
307
- else if (/\.(chaml|coffeehaml)$/.test(dir)) {
308
- files.push(dir);
342
+ target = resolve(opts.input);
343
+ stats = existsSync(target) ? statSync(target) : null;
344
+ files = [];
345
+ findFiles = function(dir) {
346
+ var entry, full, i, len, ref, results, stat;
347
+ if (!existsSync(dir)) {
348
+ return;
349
+ }
350
+ stat = statSync(dir);
351
+ if (stat.isDirectory()) {
352
+ ref = readdirSync(dir, {
353
+ withFileTypes: true
354
+ });
355
+ results = [];
356
+ for (i = 0, len = ref.length; i < len; i++) {
357
+ entry = ref[i];
358
+ full = join(dir, entry.name);
359
+ if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
360
+ results.push(findFiles(full));
361
+ } else if (entry.isFile() && /\.(chaml|coffeehaml)$/.test(entry.name)) {
362
+ results.push(files.push(full));
363
+ } else {
364
+ results.push(void 0);
365
+ }
309
366
  }
310
- }
367
+ return results;
368
+ } else if (/\.(chaml|coffeehaml)$/.test(dir)) {
369
+ return files.push(dir);
370
+ }
371
+ };
311
372
  findFiles(target);
312
373
  if (files.length === 0) {
313
- console.error(`Error: no .chaml/.coffeehaml files found in ${opts.input}`);
314
- process.exit(1);
374
+ console.error(`Error: no .chaml/.coffeehaml files found in ${opts.input}`);
375
+ process.exit(1);
315
376
  }
316
377
  console.error(`Watching ${files.length} file(s)...`);
317
- let initial = true;
318
- const compile = (file) => {
319
- const rel = relative(process.cwd(), file);
320
- if (!initial)
321
- console.error(`\n[recompile] ${rel}`);
322
- initial = false;
323
- // Derive output: same dir, .js extension
324
- const outFile = opts.output || file.replace(/\.(chaml|coffeehaml)$/, '.js');
325
- const ok = doCompile(file, { ...opts, output: outFile, input: file });
326
- if (!ok)
327
- console.error(` ✗ ${rel}`);
378
+ initial = true;
379
+ compileFn = function(file) {
380
+ var outFile, rel;
381
+ rel = relative(process.cwd(), file);
382
+ if (!initial) {
383
+ console.error(`\n[recompile] ${rel}`);
384
+ }
385
+ initial = false;
386
+ outFile = opts.output || file.replace(/\.(chaml|coffeehaml)$/, '.js');
387
+ ok = doCompile(file, {
388
+ ...opts,
389
+ output: outFile,
390
+ input: file
391
+ });
392
+ if (!ok) {
393
+ return console.error(` ✗ ${rel}`);
394
+ }
328
395
  };
329
- for (const file of files)
330
- compile(file);
331
- // Watch each file for changes (debounced)
332
- const timers = new Map();
333
- const watchDir = stats?.isDirectory() ? target : dirname(target);
334
- const watcher = watch(watchDir, { recursive: true }, (_event, filename) => {
335
- if (!filename || !/\.(chaml|coffeehaml)$/.test(filename))
336
- return;
337
- const full = resolve(watchDir, filename);
338
- if (timers.has(full))
339
- clearTimeout(timers.get(full));
340
- timers.set(full, setTimeout(() => {
341
- timers.delete(full);
342
- compile(full);
343
- }, 100));
396
+ for (i = 0, len = files.length; i < len; i++) {
397
+ file = files[i];
398
+ compileFn(file);
399
+ }
400
+ timers = new Map();
401
+ watchDir = (stats != null ? stats.isDirectory() : void 0) ? target : dirname(target);
402
+ watcher = watch(watchDir, {
403
+ recursive: true
404
+ }, function(_event, filename) {
405
+ var full;
406
+ if (!(filename && /\.(chaml|coffeehaml)$/.test(filename))) {
407
+ return;
408
+ }
409
+ full = resolve(watchDir, filename);
410
+ if (timers.has(full)) {
411
+ clearTimeout(timers.get(full));
412
+ }
413
+ return timers.set(full, setTimeout((function() {
414
+ timers.delete(full);
415
+ return compileFn(full);
416
+ }), 100));
344
417
  });
345
- process.on('SIGINT', () => {
346
- watcher.close();
347
- console.error('\nStopped watching.');
348
- process.exit(0);
418
+ process.on('SIGINT', function() {
419
+ watcher.close();
420
+ console.error('\nStopped watching.');
421
+ return process.exit(0);
349
422
  });
350
- }
351
- else if (cmd === 'init') {
423
+ break;
424
+ case 'init':
352
425
  doInit();
353
- }
354
- else {
426
+ break;
427
+ default:
355
428
  console.error(`Unknown command: ${cmd}`);
356
429
  process.exit(1);
357
430
  }
358
- //# sourceMappingURL=cli.js.map