@fluffjs/cli 0.4.4 → 0.5.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.
Files changed (47) hide show
  1. package/BabelHelpers.js +8 -5
  2. package/Cli.d.ts +9 -5
  3. package/Cli.js +218 -155
  4. package/CodeGenerator.d.ts +19 -10
  5. package/CodeGenerator.js +146 -106
  6. package/ComponentCompiler.d.ts +19 -3
  7. package/ComponentCompiler.js +175 -47
  8. package/DevServer.d.ts +6 -4
  9. package/DevServer.js +102 -23
  10. package/DomPreProcessor.js +22 -40
  11. package/IndexHtmlTransformer.js +20 -28
  12. package/PluginLoader.d.ts +22 -0
  13. package/PluginLoader.js +286 -0
  14. package/PluginManager.d.ts +39 -0
  15. package/PluginManager.js +209 -0
  16. package/TemplateParser.d.ts +5 -0
  17. package/TemplateParser.js +55 -0
  18. package/babel-plugin-directive.d.ts +12 -0
  19. package/babel-plugin-directive.js +78 -0
  20. package/babel-plugin-reactive.js +19 -14
  21. package/fluff-esbuild-plugin.js +66 -22
  22. package/interfaces/ClassTransformContext.d.ts +8 -0
  23. package/interfaces/ClassTransformContext.js +1 -0
  24. package/interfaces/CodeGenContext.d.ts +9 -0
  25. package/interfaces/CodeGenContext.js +1 -0
  26. package/interfaces/DiscoveryInfo.d.ts +15 -0
  27. package/interfaces/DiscoveryInfo.js +1 -0
  28. package/interfaces/EntryPointContext.d.ts +6 -0
  29. package/interfaces/EntryPointContext.js +1 -0
  30. package/interfaces/FluffConfigInterface.d.ts +2 -0
  31. package/interfaces/FluffPlugin.d.ts +26 -0
  32. package/interfaces/FluffPlugin.js +1 -0
  33. package/interfaces/FluffPluginOptions.d.ts +3 -0
  34. package/interfaces/FluffTarget.d.ts +1 -0
  35. package/interfaces/HtmlTransformOptions.d.ts +2 -0
  36. package/interfaces/PluginCustomTable.d.ts +6 -0
  37. package/interfaces/PluginCustomTable.js +1 -0
  38. package/interfaces/PluginHookDependency.d.ts +5 -0
  39. package/interfaces/PluginHookDependency.js +1 -0
  40. package/interfaces/PluginHookName.d.ts +2 -0
  41. package/interfaces/PluginHookName.js +1 -0
  42. package/interfaces/ScopeElementConfig.d.ts +5 -0
  43. package/interfaces/ScopeElementConfig.js +1 -0
  44. package/interfaces/index.d.ts +8 -0
  45. package/package.json +5 -3
  46. package/PeerDependencies.d.ts +0 -6
  47. package/PeerDependencies.js +0 -7
package/BabelHelpers.js CHANGED
@@ -43,23 +43,26 @@ export function parseMethodBody(body) {
43
43
  }
44
44
  return [];
45
45
  }
46
+ function buildHostElementCall() {
47
+ return t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('__getHostElement')), []);
48
+ }
46
49
  export function buildHostBindingUpdateStatement(hostProperty) {
47
50
  if (hostProperty.startsWith('class.')) {
48
51
  const className = hostProperty.slice(6);
49
- return t.ifStatement(t.identifier('__v'), t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('classList')), t.identifier('add')), [t.stringLiteral(className)])), t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('classList')), t.identifier('remove')), [t.stringLiteral(className)])));
52
+ return t.ifStatement(t.identifier('__v'), t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(buildHostElementCall(), t.identifier('classList')), t.identifier('add')), [t.stringLiteral(className)])), t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(buildHostElementCall(), t.identifier('classList')), t.identifier('remove')), [t.stringLiteral(className)])));
50
53
  }
51
54
  else if (hostProperty.startsWith('attr.')) {
52
55
  const attrName = hostProperty.slice(5);
53
- return t.ifStatement(t.binaryExpression('!=', t.identifier('__v'), t.nullLiteral()), t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('setAttribute')), [
56
+ return t.ifStatement(t.binaryExpression('!=', t.identifier('__v'), t.nullLiteral()), t.expressionStatement(t.callExpression(t.memberExpression(buildHostElementCall(), t.identifier('setAttribute')), [
54
57
  t.stringLiteral(attrName),
55
58
  t.callExpression(t.identifier('String'), [t.identifier('__v')])
56
- ])), t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('removeAttribute')), [t.stringLiteral(attrName)])));
59
+ ])), t.expressionStatement(t.callExpression(t.memberExpression(buildHostElementCall(), t.identifier('removeAttribute')), [t.stringLiteral(attrName)])));
57
60
  }
58
61
  else if (hostProperty.startsWith('style.')) {
59
62
  const styleProp = hostProperty.slice(6);
60
- return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('style')), t.identifier(styleProp)), t.logicalExpression('||', t.identifier('__v'), t.stringLiteral(''))));
63
+ return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.memberExpression(buildHostElementCall(), t.identifier('style')), t.identifier(styleProp)), t.logicalExpression('||', t.identifier('__v'), t.stringLiteral(''))));
61
64
  }
62
65
  else {
63
- return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.thisExpression(), t.identifier(hostProperty)), t.identifier('__v')));
66
+ return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(buildHostElementCall(), t.identifier(hostProperty)), t.identifier('__v')));
64
67
  }
65
68
  }
package/Cli.d.ts CHANGED
@@ -6,8 +6,12 @@ export declare class Cli {
6
6
  private readonly noMinify;
7
7
  private readonly gzScriptTag;
8
8
  constructor(options?: CliOptions);
9
- private resolveCwd;
9
+ static parseArgs(argv: string[]): {
10
+ options: CliOptions;
11
+ args: string[];
12
+ };
10
13
  run(args: string[]): Promise<void>;
14
+ private resolveCwd;
11
15
  private showHelp;
12
16
  private getProjectRoot;
13
17
  private findNxPackageRoot;
@@ -25,9 +29,13 @@ export declare class Cli {
25
29
  private serve;
26
30
  private serveTarget;
27
31
  private resolveEntryPoint;
32
+ private loadPlugins;
33
+ private cleanupTempEntry;
34
+ private buildBaseEsbuildConfig;
28
35
  private getEsbuildEntryConfig;
29
36
  private getJsBundleName;
30
37
  private collectStyles;
38
+ private collectGlobalStyles;
31
39
  private loadTsConfig;
32
40
  private runTypeCheck;
33
41
  private generateEntryContent;
@@ -39,9 +47,5 @@ export declare class Cli {
39
47
  private copyAssetsForServe;
40
48
  private copyDirectoryRecursive;
41
49
  private copyDirectoryRecursiveSync;
42
- static parseArgs(argv: string[]): {
43
- options: CliOptions;
44
- args: string[];
45
- };
46
50
  }
47
51
  //# sourceMappingURL=Cli.d.ts.map
package/Cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as t from '@babel/types';
2
2
  import { execSync } from 'child_process';
3
- import { randomUUID } from 'crypto';
3
+ import { randomBytes, randomUUID } from 'crypto';
4
4
  import * as esbuild from 'esbuild';
5
5
  import * as fs from 'fs';
6
6
  import * as path from 'path';
@@ -12,6 +12,7 @@ import { DevServer } from './DevServer.js';
12
12
  import { fluffPlugin } from './fluff-esbuild-plugin.js';
13
13
  import { Generator } from './Generator.js';
14
14
  import { IndexHtmlTransformer } from './IndexHtmlTransformer.js';
15
+ import { PluginLoader } from './PluginLoader.js';
15
16
  import { DEFAULT_CONFIG } from './types/FluffConfig.js';
16
17
  export class Cli {
17
18
  cwd;
@@ -26,12 +27,45 @@ export class Cli {
26
27
  this.noMinify = options.noMinify ?? false;
27
28
  this.gzScriptTag = options.gzScriptTag ?? false;
28
29
  }
29
- resolveCwd() {
30
- const processCwd = process.cwd();
31
- if (fs.existsSync(path.join(processCwd, 'fluff.json'))) {
32
- return processCwd;
30
+ static parseArgs(argv) {
31
+ const options = {};
32
+ const args = [];
33
+ let i = 0;
34
+ while (i < argv.length) {
35
+ const arg = argv[i];
36
+ if (arg === '--nx' && argv[i + 1]) {
37
+ options.nxPackage = argv[i + 1];
38
+ i += 2;
39
+ }
40
+ else if (arg === '--cwd' && argv[i + 1]) {
41
+ options.cwd = argv[i + 1];
42
+ i += 2;
43
+ }
44
+ else if (arg === '--no-gzip') {
45
+ options.noGzip = true;
46
+ i++;
47
+ }
48
+ else if (arg === '--no-minify') {
49
+ options.noMinify = true;
50
+ i++;
51
+ }
52
+ else if (arg === '--gz-script-tag') {
53
+ options.gzScriptTag = true;
54
+ i++;
55
+ }
56
+ else if (arg?.startsWith('--')) {
57
+ console.error(`Unknown option: ${arg}`);
58
+ process.exit(1);
59
+ }
60
+ else if (arg) {
61
+ args.push(arg);
62
+ i++;
63
+ }
64
+ else {
65
+ i++;
66
+ }
33
67
  }
34
- return process.env.INIT_CWD ?? processCwd;
68
+ return { options, args };
35
69
  }
36
70
  async run(args) {
37
71
  const [command, ...commandArgs] = args;
@@ -61,6 +95,13 @@ export class Cli {
61
95
  process.exit(1);
62
96
  }
63
97
  }
98
+ resolveCwd() {
99
+ const processCwd = process.cwd();
100
+ if (fs.existsSync(path.join(processCwd, 'fluff.json'))) {
101
+ return processCwd;
102
+ }
103
+ return process.env.INIT_CWD ?? processCwd;
104
+ }
64
105
  showHelp() {
65
106
  console.log(`
66
107
  Fluff CLI - Build tool for Fluff components
@@ -253,10 +294,7 @@ Examples:
253
294
  if (targetName) {
254
295
  config.targets = {
255
296
  [targetName]: {
256
- name: targetName,
257
- srcDir: 'src',
258
- outDir: 'dist',
259
- assets: ['**/*.html', '**/*.css']
297
+ name: targetName, srcDir: 'src', outDir: 'dist', assets: ['**/*.html', '**/*.css']
260
298
  }
261
299
  };
262
300
  config.defaultTarget = targetName;
@@ -289,9 +327,7 @@ Examples:
289
327
  }
290
328
  const generator = new Generator();
291
329
  generator.generate({
292
- appName,
293
- outputDir: this.cwd,
294
- packageManager
330
+ appName, outputDir: this.cwd, packageManager
295
331
  });
296
332
  }
297
333
  async build(args) {
@@ -314,6 +350,7 @@ Examples:
314
350
  throw new Error(`fluff.json not found at ${configPath}. Run 'fluff init' first.`);
315
351
  }
316
352
  const config = this.loadConfigFrom(configPath);
353
+ const pluginManager = await this.loadPlugins(config, projectRoot);
317
354
  let targets = [];
318
355
  if (targetOrProject) {
319
356
  const target = config.targets[targetOrProject];
@@ -337,16 +374,14 @@ Examples:
337
374
  targets = Object.values(config.targets);
338
375
  }
339
376
  for (const target of targets) {
340
- await this.buildTarget(target, projectRoot, workspaceRoot, projectRelativePath);
377
+ await this.buildTarget(target, projectRoot, workspaceRoot, projectRelativePath, pluginManager);
341
378
  }
342
379
  }
343
- async buildTarget(target, projectRoot, workspaceRoot, projectRelativePath) {
380
+ async buildTarget(target, projectRoot, workspaceRoot, projectRelativePath, pluginManager) {
344
381
  console.log(`🔨 Building target '${target.name}'...`);
345
382
  const srcDir = path.resolve(projectRoot, target.srcDir);
346
383
  const appDir = path.join(srcDir, target.componentsDir ?? 'app');
347
- const outDir = (workspaceRoot && projectRelativePath)
348
- ? path.join(workspaceRoot, 'dist', projectRelativePath)
349
- : path.resolve(projectRoot, target.outDir);
384
+ const outDir = (workspaceRoot && projectRelativePath) ? path.join(workspaceRoot, 'dist', projectRelativePath) : path.resolve(projectRoot, target.outDir);
350
385
  if (!fs.existsSync(srcDir)) {
351
386
  throw new Error(`Source directory not found: ${srcDir}`);
352
387
  }
@@ -360,42 +395,31 @@ Examples:
360
395
  if (this.noMinify) {
361
396
  bundleOptions.minify = false;
362
397
  }
363
- const entry = this.resolveEntryPoint(target, srcDir);
398
+ const splitting = bundleOptions.splitting ?? false;
399
+ const entry = this.resolveEntryPoint(target, srcDir, splitting);
364
400
  const inlineStyles = await this.collectStyles(target, srcDir, bundleOptions.minify ?? true);
401
+ const globalStylesCss = await this.collectGlobalStyles(target, srcDir, bundleOptions.minify ?? true);
365
402
  this.runTypeCheck(target, projectRoot);
366
403
  console.log(' Building with esbuild...');
367
404
  const tsconfigRaw = this.loadTsConfig(target, projectRoot);
368
- const result = await esbuild.build({
369
- ...this.getEsbuildEntryConfig(entry),
370
- bundle: true,
371
- ...(entry.useStdin
372
- ? { outfile: path.join(outDir, 'fluff-app.js') }
373
- : { outdir: outDir, entryNames: '[name]' }),
374
- format: 'esm',
375
- platform: 'browser',
376
- target: bundleOptions.target ?? 'es2022',
405
+ const result = await esbuild.build(this.buildBaseEsbuildConfig({
406
+ entry, appDir, outDir, splitting,
377
407
  minify: bundleOptions.minify ?? true,
378
- splitting: bundleOptions.splitting ?? false,
379
- treeShaking: true,
408
+ tsconfigRaw, pluginManager,
409
+ production: true,
410
+ target: bundleOptions.target ?? 'es2022',
380
411
  metafile: true,
381
- plugins: [
382
- fluffPlugin({
383
- srcDir: appDir,
384
- outDir,
385
- minify: bundleOptions.minify ?? true,
386
- skipDefine: false,
387
- production: true
388
- })
389
- ],
390
412
  external: bundleOptions.external ?? [],
391
- logLevel: 'warning',
392
- tsconfigRaw
413
+ globalStylesCss
414
+ }))
415
+ .finally(() => {
416
+ this.cleanupTempEntry(entry);
393
417
  });
394
- const outputs = Object.keys(result.metafile?.outputs ?? {});
395
- const jsBundle = outputs.find(f => f.endsWith('.js'));
396
- const cssBundle = outputs.find(f => f.endsWith('.css'));
418
+ const outputKeys = Object.keys(result.metafile?.outputs ?? {});
419
+ const jsBundleName = this.getJsBundleName(entry);
420
+ const jsBundle = outputKeys.find(f => path.basename(f) === jsBundleName);
421
+ const cssBundle = outputKeys.find(f => f.endsWith('.css'));
397
422
  if (jsBundle) {
398
- const jsBundleName = path.basename(jsBundle);
399
423
  const jsPath = path.join(outDir, jsBundleName);
400
424
  if (bundleOptions.gzip) {
401
425
  const gzipContent = fs.readFileSync(jsPath);
@@ -432,7 +456,8 @@ Examples:
432
456
  inlineStyles: inlineStyles || undefined,
433
457
  gzip: bundleOptions.gzip,
434
458
  gzScriptTag: bundleOptions.gzScriptTag ?? this.gzScriptTag,
435
- minify: bundleOptions.minify
459
+ minify: bundleOptions.minify,
460
+ pluginManager
436
461
  });
437
462
  fs.writeFileSync(path.join(outDir, 'index.html'), transformed);
438
463
  console.log(' ✓ Transformed index.html');
@@ -463,6 +488,7 @@ Examples:
463
488
  throw new Error(`fluff.json not found at ${configPath}. Run 'fluff init' first.`);
464
489
  }
465
490
  const config = this.loadConfigFrom(configPath);
491
+ const pluginManager = await this.loadPlugins(config, projectRoot);
466
492
  let target = undefined;
467
493
  if (targetOrProject) {
468
494
  target = config.targets[targetOrProject];
@@ -485,9 +511,9 @@ Examples:
485
511
  process.exit(1);
486
512
  }
487
513
  }
488
- await this.serveTarget(target, projectRoot, workspaceRoot, projectRelativePath);
514
+ await this.serveTarget(target, projectRoot, workspaceRoot, projectRelativePath, pluginManager);
489
515
  }
490
- async serveTarget(target, projectRoot, workspaceRoot, projectRelativePath) {
516
+ async serveTarget(target, projectRoot, workspaceRoot, projectRelativePath, pluginManager) {
491
517
  const srcDir = path.resolve(projectRoot, target.srcDir);
492
518
  const appDir = path.join(srcDir, target.componentsDir ?? 'app');
493
519
  const fluffDir = path.join(this.cwd, '.fluff');
@@ -496,8 +522,17 @@ Examples:
496
522
  if (!fs.existsSync(outDir)) {
497
523
  fs.mkdirSync(outDir, { recursive: true });
498
524
  }
525
+ let entry = null;
526
+ let ctx = null;
499
527
  const cleanup = () => {
500
528
  try {
529
+ if (ctx) {
530
+ ctx.dispose().catch((e) => {
531
+ console.error('Failed to dispose esbuild context:', e);
532
+ });
533
+ ctx = null;
534
+ }
535
+ this.cleanupTempEntry(entry);
501
536
  if (fs.existsSync(outDir)) {
502
537
  fs.rmSync(outDir, { recursive: true, force: true });
503
538
  }
@@ -517,7 +552,6 @@ Examples:
517
552
  const serveOptions = target.serve ?? {};
518
553
  const port = serveOptions.port ?? 3000;
519
554
  const host = serveOptions.host ?? 'localhost';
520
- const esbuildPort = port + 1;
521
555
  let proxyConfig = undefined;
522
556
  if (serveOptions.proxyConfig) {
523
557
  const proxyConfigPath = path.resolve(projectRoot, serveOptions.proxyConfig);
@@ -529,8 +563,11 @@ Examples:
529
563
  }
530
564
  }
531
565
  }
532
- const entry = this.resolveEntryPoint(target, srcDir);
566
+ const bundleOptions = { ...target.bundle };
567
+ const splitting = bundleOptions.splitting ?? false;
568
+ entry = this.resolveEntryPoint(target, srcDir, splitting);
533
569
  const inlineStyles = await this.collectStyles(target, srcDir, false);
570
+ const globalStylesCss = await this.collectGlobalStyles(target, srcDir, false);
534
571
  if (target.indexHtml) {
535
572
  const indexHtmlPath = path.join(srcDir, target.indexHtml);
536
573
  if (fs.existsSync(indexHtmlPath)) {
@@ -541,7 +578,8 @@ Examples:
541
578
  inlineStyles: inlineStyles || undefined,
542
579
  gzip: false,
543
580
  minify: false,
544
- liveReload: true
581
+ liveReload: true,
582
+ pluginManager
545
583
  });
546
584
  fs.writeFileSync(path.join(outDir, 'index.html'), transformed);
547
585
  }
@@ -551,73 +589,104 @@ Examples:
551
589
  }
552
590
  console.log(`🚀 Starting dev server for '${target.name}'...`);
553
591
  const tsconfigRaw = this.loadTsConfig(target, projectRoot);
554
- const ctx = await esbuild.context({
555
- ...this.getEsbuildEntryConfig(entry),
592
+ const devServer = new DevServer({
593
+ port, host, outDir, proxyConfig
594
+ });
595
+ ctx = await esbuild.context(this.buildBaseEsbuildConfig({
596
+ entry, appDir, outDir, splitting,
597
+ minify: false,
598
+ tsconfigRaw, pluginManager,
599
+ production: false,
600
+ sourcemap: true,
601
+ globalStylesCss,
602
+ extraPlugins: [{
603
+ name: 'fluff-live-reload', setup(build) {
604
+ build.onEnd((result) => {
605
+ if (result.errors.length === 0) {
606
+ console.log('[watch] build finished, watching for changes...');
607
+ devServer.notifyReload();
608
+ }
609
+ });
610
+ }
611
+ }]
612
+ }));
613
+ await ctx.watch();
614
+ console.log(' Watching for changes...');
615
+ await devServer.start();
616
+ console.log(` Server running at http://${host}:${port}`);
617
+ console.log(' Press Ctrl+C to stop\n');
618
+ }
619
+ resolveEntryPoint(target, srcDir, splitting) {
620
+ if (!target.entryPoint) {
621
+ const generated = this.generateEntryContent(srcDir, target.exclude);
622
+ if (splitting) {
623
+ let tempPath = path.join(srcDir, 'fluff-app.ts');
624
+ if (fs.existsSync(tempPath)) {
625
+ const hex = randomBytes(4).toString('hex');
626
+ tempPath = path.join(srcDir, `fluff-app-${hex}.ts`);
627
+ }
628
+ fs.writeFileSync(tempPath, generated.contents);
629
+ return { useStdin: false, entryPointFile: tempPath, generatedEntry: null, tempEntryFile: tempPath };
630
+ }
631
+ return { useStdin: true, entryPointFile: null, generatedEntry: generated, tempEntryFile: null };
632
+ }
633
+ return {
634
+ useStdin: false,
635
+ entryPointFile: path.join(srcDir, target.entryPoint),
636
+ generatedEntry: null,
637
+ tempEntryFile: null
638
+ };
639
+ }
640
+ async loadPlugins(config, projectRoot) {
641
+ const pluginManager = await PluginLoader.load(config, projectRoot);
642
+ if (pluginManager.hasPlugins) {
643
+ await pluginManager.runAfterConfig(config, config.pluginConfig ?? {});
644
+ }
645
+ return pluginManager;
646
+ }
647
+ cleanupTempEntry(entry) {
648
+ if (entry?.tempEntryFile && fs.existsSync(entry.tempEntryFile)) {
649
+ fs.unlinkSync(entry.tempEntryFile);
650
+ }
651
+ }
652
+ buildBaseEsbuildConfig(params) {
653
+ return {
654
+ ...this.getEsbuildEntryConfig(params.entry),
556
655
  bundle: true,
557
- ...(entry.useStdin
558
- ? { outfile: path.join(outDir, 'fluff-app.js') }
559
- : { outdir: outDir, entryNames: '[name]' }),
656
+ ...(params.entry.useStdin
657
+ ? { outfile: path.join(params.outDir, 'fluff-app.js') }
658
+ : { outdir: params.outDir, entryNames: '[name]' }),
560
659
  format: 'esm',
561
660
  platform: 'browser',
562
- target: 'es2022',
563
- minify: false,
661
+ target: params.target ?? 'es2022',
662
+ minify: params.minify,
663
+ splitting: params.splitting,
564
664
  treeShaking: true,
565
- sourcemap: true,
665
+ metafile: params.metafile ?? false,
666
+ sourcemap: params.sourcemap ?? false,
566
667
  plugins: [
567
668
  fluffPlugin({
568
- srcDir: appDir,
569
- outDir,
570
- minify: false,
571
- sourcemap: true,
669
+ srcDir: params.appDir,
670
+ outDir: params.outDir,
671
+ minify: params.minify,
672
+ sourcemap: params.sourcemap,
572
673
  skipDefine: false,
573
- production: false
574
- })
674
+ production: params.production,
675
+ pluginManager: params.pluginManager,
676
+ globalStylesCss: params.globalStylesCss
677
+ }),
678
+ ...(params.extraPlugins ?? [])
575
679
  ],
576
- logLevel: 'info',
577
- tsconfigRaw
578
- });
579
- await ctx.watch();
580
- console.log(' Watching for changes...');
581
- if (proxyConfig) {
582
- await ctx.serve({
583
- servedir: outDir,
584
- port: esbuildPort,
585
- host: '127.0.0.1'
586
- });
587
- const devServer = new DevServer({
588
- port,
589
- host,
590
- esbuildPort,
591
- esbuildHost: '127.0.0.1',
592
- proxyConfig
593
- });
594
- await devServer.start();
595
- console.log(` Server running at http://${host}:${port}`);
596
- console.log(' Press Ctrl+C to stop\n');
597
- }
598
- else {
599
- const { hosts, port: actualPort } = await ctx.serve({
600
- servedir: outDir,
601
- port,
602
- host
603
- });
604
- console.log(` Server running at http://${hosts[0]}:${actualPort}`);
605
- console.log(' Press Ctrl+C to stop\n');
606
- }
607
- }
608
- resolveEntryPoint(target, srcDir) {
609
- const useStdin = !target.entryPoint;
610
- const entryPointFile = target.entryPoint ? path.join(srcDir, target.entryPoint) : null;
611
- const generatedEntry = useStdin ? this.generateEntryContent(srcDir, target.exclude) : null;
612
- return { useStdin, entryPointFile, generatedEntry };
680
+ external: params.external ?? [],
681
+ logLevel: 'warning',
682
+ tsconfigRaw: params.tsconfigRaw
683
+ };
613
684
  }
614
685
  getEsbuildEntryConfig(entry) {
615
686
  if (entry.useStdin && entry.generatedEntry) {
616
687
  return {
617
688
  stdin: {
618
- contents: entry.generatedEntry.contents,
619
- resolveDir: entry.generatedEntry.resolveDir,
620
- loader: 'ts'
689
+ contents: entry.generatedEntry.contents, resolveDir: entry.generatedEntry.resolveDir, loader: 'ts'
621
690
  }
622
691
  };
623
692
  }
@@ -627,7 +696,8 @@ Examples:
627
696
  if (entry.useStdin) {
628
697
  return 'fluff-app.js';
629
698
  }
630
- return path.basename(entry.entryPointFile ?? '').replace('.ts', '.js');
699
+ return path.basename(entry.entryPointFile ?? '')
700
+ .replace(/\.ts$/, '.js');
631
701
  }
632
702
  async collectStyles(target, srcDir, minify) {
633
703
  if (!target.styles || target.styles.length === 0) {
@@ -636,7 +706,8 @@ Examples:
636
706
  const styleContents = [];
637
707
  for (const stylePath of target.styles) {
638
708
  const fullPath = path.resolve(srcDir, stylePath);
639
- if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
709
+ if (fs.existsSync(fullPath) && fs.statSync(fullPath)
710
+ .isFile()) {
640
711
  console.log(` ✓ Style: ${stylePath}`);
641
712
  styleContents.push(fs.readFileSync(fullPath, 'utf-8'));
642
713
  }
@@ -657,18 +728,51 @@ Examples:
657
728
  let inlineStyles = styleContents.join('\n');
658
729
  if (minify) {
659
730
  const cssResult = await esbuild.transform(inlineStyles, {
660
- loader: 'css',
661
- minify: true
731
+ loader: 'css', minify: true
662
732
  });
663
733
  inlineStyles = cssResult.code;
664
734
  }
665
735
  console.log(' ✓ Bundled global styles');
666
736
  return inlineStyles;
667
737
  }
738
+ async collectGlobalStyles(target, srcDir, minify) {
739
+ if (!target.globalStyles || target.globalStyles.length === 0) {
740
+ return '';
741
+ }
742
+ const styleContents = [];
743
+ for (const stylePath of target.globalStyles) {
744
+ const fullPath = path.resolve(srcDir, stylePath);
745
+ if (fs.existsSync(fullPath) && fs.statSync(fullPath)
746
+ .isFile()) {
747
+ console.log(` ✓ Global style: ${stylePath}`);
748
+ styleContents.push(fs.readFileSync(fullPath, 'utf-8'));
749
+ }
750
+ else {
751
+ const styleFiles = this.findFiles(srcDir, [stylePath]);
752
+ if (styleFiles.length === 0) {
753
+ console.warn(` ⚠ Global style not found: ${fullPath}`);
754
+ }
755
+ for (const styleFile of styleFiles) {
756
+ console.log(` ✓ Global style: ${path.relative(srcDir, styleFile)}`);
757
+ styleContents.push(fs.readFileSync(styleFile, 'utf-8'));
758
+ }
759
+ }
760
+ }
761
+ if (styleContents.length === 0) {
762
+ return '';
763
+ }
764
+ let css = styleContents.join('\n');
765
+ if (minify) {
766
+ const cssResult = await esbuild.transform(css, {
767
+ loader: 'css', minify: true
768
+ });
769
+ css = cssResult.code;
770
+ }
771
+ console.log(' ✓ Bundled global component styles');
772
+ return css;
773
+ }
668
774
  loadTsConfig(target, projectRoot) {
669
- return target.tsConfigPath
670
- ? fs.readFileSync(path.resolve(projectRoot, target.tsConfigPath), 'utf-8')
671
- : '{}';
775
+ return target.tsConfigPath ? fs.readFileSync(path.resolve(projectRoot, target.tsConfigPath), 'utf-8') : '{}';
672
776
  }
673
777
  runTypeCheck(target, projectRoot) {
674
778
  if (!target.tsConfigPath) {
@@ -682,8 +786,7 @@ Examples:
682
786
  console.log(' Checking types...');
683
787
  try {
684
788
  execSync(`npx -p typescript tsc --noEmit -p ${target.tsConfigPath}`, {
685
- cwd: projectRoot,
686
- stdio: 'inherit'
789
+ cwd: projectRoot, stdio: 'inherit'
687
790
  });
688
791
  console.log(' ✓ Type check passed');
689
792
  }
@@ -709,7 +812,7 @@ Examples:
709
812
  }
710
813
  findAllTsFiles(dir, userExclude = []) {
711
814
  const files = [];
712
- const excludePatterns = ['*.spec.ts', '*.test.ts', '__generated_entry.ts', ...userExclude];
815
+ const excludePatterns = ['*.spec.ts', '*.test.ts', 'fluff-app*.ts', ...userExclude];
713
816
  const walk = (currentDir) => {
714
817
  const entries = fs.readdirSync(currentDir, { withFileTypes: true });
715
818
  for (const entry of entries) {
@@ -888,44 +991,4 @@ Examples:
888
991
  }
889
992
  }
890
993
  }
891
- static parseArgs(argv) {
892
- const options = {};
893
- const args = [];
894
- let i = 0;
895
- while (i < argv.length) {
896
- const arg = argv[i];
897
- if (arg === '--nx' && argv[i + 1]) {
898
- options.nxPackage = argv[i + 1];
899
- i += 2;
900
- }
901
- else if (arg === '--cwd' && argv[i + 1]) {
902
- options.cwd = argv[i + 1];
903
- i += 2;
904
- }
905
- else if (arg === '--no-gzip') {
906
- options.noGzip = true;
907
- i++;
908
- }
909
- else if (arg === '--no-minify') {
910
- options.noMinify = true;
911
- i++;
912
- }
913
- else if (arg === '--gz-script-tag') {
914
- options.gzScriptTag = true;
915
- i++;
916
- }
917
- else if (arg?.startsWith('--')) {
918
- console.error(`Unknown option: ${arg}`);
919
- process.exit(1);
920
- }
921
- else if (arg) {
922
- args.push(arg);
923
- i++;
924
- }
925
- else {
926
- i++;
927
- }
928
- }
929
- return { options, args };
930
- }
931
994
  }