coffeehaml 0.7.1 → 0.7.3

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/src/cli.js DELETED
@@ -1,430 +0,0 @@
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()}
57
- Haml structure. CoffeeScript semantics. React runtime.
58
-
59
- Usage:
60
- coffeehaml init scaffold a new project
61
- coffeehaml compile <input> [-o <output>] [--source-map] [--wrap <mode>]
62
- coffeehaml watch <input> [-o <output>] [--source-map] [--wrap <mode>]
63
-
64
- Options:
65
- -o, --output <file> Write output to file instead of stdout
66
- --source-map Emit inline source map
67
- --wrap <mode> Wrap output: 'component', 'observer', or comma-separated HOC list
68
- -h, --help Show this help
69
-
70
- Examples:
71
- coffeehaml init
72
- coffeehaml compile app.chaml -o app.js --wrap component
73
- coffeehaml compile app.chaml # prints to stdout
74
- coffeehaml watch src/ --wrap observer # watch and recompile on change`);
75
- process.exit(0);
76
- }
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 {
126
- return `Warning: ${warn.message}`;
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));
142
- }
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);
154
- }
155
- console.error(`Compiled: ${input} → ${opts.output}`);
156
- } else {
157
- process.stdout.write(result.code);
158
- }
159
- return true;
160
- };
161
-
162
- // ─── Init ──────────────────────────────────────────────────
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());
173
- });
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');
236
- }
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';
241
- if (hasViteConfig) {
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()],');
252
- }
253
- }
254
- writeFileSync(configPath, existing);
255
- console.log(` ✓ Updated ${relative(cwd, configPath)}\n`);
256
- } else {
257
- content = `import { defineConfig } from 'vite';
258
- import react from '@vitejs/plugin-react';
259
- import coffeehaml from 'coffeehaml/vite-plugin';
260
-
261
- export default defineConfig({
262
- plugins: [
263
- react(),
264
- coffeehaml(),
265
- ],
266
- });`;
267
- writeFileSync(configPath, content);
268
- console.log(` ✓ Created ${relative(cwd, configPath)}\n`);
269
- }
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
283
- import { useState } from 'react'
284
-
285
- %div.app
286
- %header
287
- %h1= "CoffeeHaml ✨"
288
- %p.subtitle Haml structure. CoffeeScript semantics. React runtime.
289
-
290
- %main
291
- - name = 'World'
292
- %section.card
293
- %h2= "Hello, \#{name}!"
294
- %p= "Counter: 0"
295
-
296
- %footer
297
- %small Built with coffeehaml v${getVersion()}`;
298
- writeFileSync(samplePath, sample);
299
- console.log(` ✓ Created ${relative(cwd, samplePath)}\n`);
300
- }
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/';
308
- }
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);
329
- if (!opts.input) {
330
- console.error('Error: no input file specified');
331
- process.exit(1);
332
- }
333
- ok = doCompile(opts.input, opts);
334
- process.exit(ok ? 0 : 1);
335
- break;
336
- case 'watch':
337
- opts = parseArgs(rest);
338
- if (!opts.input) {
339
- console.error('Error: no input path specified');
340
- process.exit(1);
341
- }
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
- }
366
- }
367
- return results;
368
- } else if (/\.(chaml|coffeehaml)$/.test(dir)) {
369
- return files.push(dir);
370
- }
371
- };
372
- findFiles(target);
373
- if (files.length === 0) {
374
- console.error(`Error: no .chaml/.coffeehaml files found in ${opts.input}`);
375
- process.exit(1);
376
- }
377
- console.error(`Watching ${files.length} file(s)...`);
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
- }
395
- };
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));
417
- });
418
- process.on('SIGINT', function() {
419
- watcher.close();
420
- console.error('\nStopped watching.');
421
- return process.exit(0);
422
- });
423
- break;
424
- case 'init':
425
- doInit();
426
- break;
427
- default:
428
- console.error(`Unknown command: ${cmd}`);
429
- process.exit(1);
430
- }
@@ -1,132 +0,0 @@
1
- // Generated by CoffeeScript 2.7.0
2
- // compiler.coffee — compile pipeline orchestration
3
- var wrapError;
4
-
5
- import {
6
- tokenize
7
- } from './lexer.js';
8
-
9
- import {
10
- parse
11
- } from './parser.js';
12
-
13
- import {
14
- emit
15
- } from './emitter.js';
16
-
17
- import {
18
- CompileError
19
- } from './types.js';
20
-
21
- import {
22
- isCoffeeScriptAvailable,
23
- getCoffeeScriptUnavailableReason
24
- } from './expressions.js';
25
-
26
- import {
27
- readFileSync,
28
- existsSync
29
- } from 'fs';
30
-
31
- // Supported extensions
32
- export var COFFEEHAML_EXTENSIONS = ['.coffeehaml', '.cohaml', '.chaml'];
33
-
34
- // Resolve a CoffeeHaml file path
35
- export var resolveCoffeeHamlFile = function(filepath) {
36
- var ext, i, len, withExt;
37
- if (existsSync(filepath)) {
38
- return filepath;
39
- }
40
- for (i = 0, len = COFFEEHAML_EXTENSIONS.length; i < len; i++) {
41
- ext = COFFEEHAML_EXTENSIONS[i];
42
- withExt = filepath + ext;
43
- if (existsSync(withExt)) {
44
- return withExt;
45
- }
46
- }
47
- return null;
48
- };
49
-
50
- // Compile CoffeeHaml source to JavaScript
51
- export var compile = function(source, options = {}) {
52
- var ast, e, errors, parsed, result, tokens, warnings;
53
- errors = [];
54
- warnings = [];
55
- // Warn if CoffeeScript unavailable
56
- if (!isCoffeeScriptAvailable()) {
57
- warnings.push({
58
- message: `CoffeeScript expression compiler unavailable: ${getCoffeeScriptUnavailableReason()}. CoffeeScript expressions will passthrough raw and may produce invalid JavaScript. Install coffeescript as a peer dependency.`,
59
- phase: 'compiler'
60
- });
61
- }
62
- try {
63
- // Phase 1: Lex
64
- tokens = tokenize(source, options.filename);
65
- } catch (error) {
66
- e = error;
67
- return {
68
- code: '',
69
- errors: [wrapError(e, 'lexer')],
70
- warnings: []
71
- };
72
- }
73
- // Phase 2: Parse
74
- parsed = parse(tokens, options.filename);
75
- ast = parsed.document;
76
- if (parsed.errors.length > 0) {
77
- errors.push(...parsed.errors);
78
- if (ast.children.length === 0) {
79
- return {
80
- code: '',
81
- errors,
82
- warnings
83
- };
84
- }
85
- }
86
- try {
87
- // Phase 3: Emit
88
- result = emit(ast, {
89
- sourceMap: options.sourceMap,
90
- filename: options.filename,
91
- jsxRuntime: options.jsxRuntime,
92
- filters: options.filters,
93
- wrap: options.wrap,
94
- componentName: options.componentName
95
- });
96
- } catch (error) {
97
- e = error;
98
- return {
99
- code: '',
100
- errors: [wrapError(e, 'emitter')],
101
- warnings
102
- };
103
- }
104
- if (result.warnings) {
105
- warnings.push(...result.warnings);
106
- }
107
- return {
108
- code: result.code,
109
- sourceMap: result.sourceMap,
110
- errors,
111
- warnings
112
- };
113
- };
114
-
115
- // Compile from disk
116
- export var compileFile = function(filepath, options = {}) {
117
- var source;
118
- source = readFileSync(filepath, 'utf-8');
119
- return compile(source, {
120
- ...options,
121
- filename: filepath
122
- });
123
- };
124
-
125
- // ─── Helpers ───────────────────────────────────────────────
126
- wrapError = function(e, phase) {
127
- if (e instanceof CompileError) {
128
- e.phase = phase;
129
- return e;
130
- }
131
- return new CompileError(e instanceof Error ? e.message : String(e), phase, 'INTERNAL_ERROR');
132
- };