@fluffjs/cli 0.3.3 → 0.3.5

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/Cli.js CHANGED
@@ -79,9 +79,13 @@ Options:
79
79
  --cwd <dir> Set working directory
80
80
  --no-gzip Disable gzip compression (overrides config)
81
81
 
82
+ Generate Options:
83
+ --packageManager, -p Package manager to use (npm, yarn, pnpm, bun)
84
+
82
85
  Examples:
83
86
  fluff init Create fluff.json with default configuration
84
87
  fluff generate my-app Create a new Fluff app called 'my-app'
88
+ fluff generate my-app --packageManager pnpm Create app and install with pnpm
85
89
  fluff build Build the default target
86
90
  fluff build app Build the 'app' target
87
91
  fluff --nx @myorg/app build Build an nx package
@@ -263,16 +267,31 @@ Examples:
263
267
  console.log(`Configuration saved to ${configPath}`);
264
268
  }
265
269
  generate(args) {
266
- const [appName] = args;
270
+ let appName = undefined;
271
+ let packageManager = undefined;
272
+ for (let i = 0; i < args.length; i++) {
273
+ const arg = args[i];
274
+ if (arg === '--packageManager' || arg === '-p') {
275
+ const nextArg = args[i + 1];
276
+ if (nextArg === 'npm' || nextArg === 'yarn' || nextArg === 'pnpm' || nextArg === 'bun') {
277
+ packageManager = nextArg;
278
+ i++;
279
+ }
280
+ }
281
+ else if (!arg.startsWith('-')) {
282
+ appName = arg;
283
+ }
284
+ }
267
285
  if (!appName) {
268
- console.error('Usage: fluff generate <app-name>');
269
- console.error('Example: fluff generate my-app');
286
+ console.error('Usage: fluff generate <app-name> [--packageManager npm|yarn|pnpm|bun]');
287
+ console.error('Example: fluff generate my-app --packageManager npm');
270
288
  process.exit(1);
271
289
  }
272
290
  const generator = new Generator();
273
291
  generator.generate({
274
292
  appName,
275
- outputDir: this.cwd
293
+ outputDir: this.cwd,
294
+ packageManager
276
295
  });
277
296
  }
278
297
  async build(args) {
@@ -13,6 +13,7 @@ import importsPlugin from './babel-plugin-imports.js';
13
13
  import reactivePlugin, { reactivePropertiesMap } from './babel-plugin-reactive.js';
14
14
  import { generate } from './BabelHelpers.js';
15
15
  import { CodeGenerator } from './CodeGenerator.js';
16
+ import { DecoratorValidator } from './DecoratorValidator.js';
16
17
  import { ErrorHelpers } from './ErrorHelpers.js';
17
18
  import { GetterDependencyExtractor } from './GetterDependencyExtractor.js';
18
19
  import { Parse5Helpers } from './Parse5Helpers.js';
@@ -154,6 +155,7 @@ export class ComponentCompiler {
154
155
  if (!skipDefine) {
155
156
  result = this.addCustomElementsDefine(result, selector, className);
156
157
  }
158
+ DecoratorValidator.validate(result, filePath);
157
159
  const tsResult = await this.stripTypeScriptWithSourceMap(result, filePath, sourcemap);
158
160
  const watchFiles = [];
159
161
  if (templatePath) {
@@ -0,0 +1,10 @@
1
+ export declare class DecoratorValidator {
2
+ static validate(code: string, filePath: string): void;
3
+ private static findDecorators;
4
+ private static checkNode;
5
+ private static checkClassDeclaration;
6
+ private static checkClassMember;
7
+ private static getMemberName;
8
+ private static getDecoratorName;
9
+ }
10
+ //# sourceMappingURL=DecoratorValidator.d.ts.map
@@ -0,0 +1,94 @@
1
+ import { parse } from '@babel/parser';
2
+ import * as t from '@babel/types';
3
+ export class DecoratorValidator {
4
+ static validate(code, filePath) {
5
+ const decorators = DecoratorValidator.findDecorators(code);
6
+ if (decorators.length > 0) {
7
+ const details = decorators.map(d => ` - @${d.name} on ${d.target} '${d.targetName}' at line ${d.line}, column ${d.column}`).join('\n');
8
+ throw new Error(`Decorators must be stripped before output but ${decorators.length} decorator(s) remain in ${filePath}:\n${details}`);
9
+ }
10
+ }
11
+ static findDecorators(code) {
12
+ const results = [];
13
+ let ast = null;
14
+ try {
15
+ ast = parse(code, {
16
+ sourceType: 'module',
17
+ plugins: ['typescript', 'decorators']
18
+ });
19
+ }
20
+ catch {
21
+ return results;
22
+ }
23
+ if (!ast) {
24
+ return results;
25
+ }
26
+ for (const node of ast.program.body) {
27
+ DecoratorValidator.checkNode(node, results);
28
+ }
29
+ return results;
30
+ }
31
+ static checkNode(node, results) {
32
+ if (t.isExportNamedDeclaration(node) && node.declaration) {
33
+ DecoratorValidator.checkNode(node.declaration, results);
34
+ return;
35
+ }
36
+ if (t.isExportDefaultDeclaration(node) && t.isClassDeclaration(node.declaration)) {
37
+ DecoratorValidator.checkClassDeclaration(node.declaration, results);
38
+ return;
39
+ }
40
+ if (t.isClassDeclaration(node)) {
41
+ DecoratorValidator.checkClassDeclaration(node, results);
42
+ }
43
+ }
44
+ static checkClassDeclaration(node, results) {
45
+ const className = node.id?.name ?? '<anonymous>';
46
+ if (node.decorators && node.decorators.length > 0) {
47
+ for (const decorator of node.decorators) {
48
+ results.push({
49
+ name: DecoratorValidator.getDecoratorName(decorator),
50
+ line: decorator.loc?.start.line ?? 0,
51
+ column: decorator.loc?.start.column ?? 0,
52
+ target: 'class',
53
+ targetName: className
54
+ });
55
+ }
56
+ }
57
+ for (const member of node.body.body) {
58
+ DecoratorValidator.checkClassMember(member, className, results);
59
+ }
60
+ }
61
+ static checkClassMember(member, className, results) {
62
+ if (!('decorators' in member) || !member.decorators || member.decorators.length === 0) {
63
+ return;
64
+ }
65
+ const memberName = DecoratorValidator.getMemberName(member);
66
+ const target = t.isClassMethod(member) ? 'method' : 'property';
67
+ for (const decorator of member.decorators) {
68
+ results.push({
69
+ name: DecoratorValidator.getDecoratorName(decorator),
70
+ line: decorator.loc?.start.line ?? 0,
71
+ column: decorator.loc?.start.column ?? 0,
72
+ target,
73
+ targetName: `${className}.${memberName}`
74
+ });
75
+ }
76
+ }
77
+ static getMemberName(member) {
78
+ if ('key' in member && t.isIdentifier(member.key)) {
79
+ return member.key.name;
80
+ }
81
+ return '<unknown>';
82
+ }
83
+ static getDecoratorName(decorator) {
84
+ if (t.isCallExpression(decorator.expression)) {
85
+ if (t.isIdentifier(decorator.expression.callee)) {
86
+ return decorator.expression.callee.name;
87
+ }
88
+ }
89
+ else if (t.isIdentifier(decorator.expression)) {
90
+ return decorator.expression.name;
91
+ }
92
+ return '<unknown>';
93
+ }
94
+ }
package/Generator.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as t from '@babel/types';
2
+ import { execSync } from 'child_process';
2
3
  import * as fs from 'fs';
3
4
  import * as parse5 from 'parse5';
4
5
  import * as path from 'path';
@@ -35,13 +36,26 @@ export class Generator {
35
36
  console.log(` ✓ Created src/app/${kebabName}.component.ts`);
36
37
  console.log(` ✓ Created src/app/${kebabName}.component.html`);
37
38
  console.log(` ✓ Created src/app/${kebabName}.component.css`);
38
- console.log('');
39
- console.log('✅ App created successfully!');
40
- console.log('');
41
- console.log('Next steps:');
42
- console.log(` cd ${kebabName}`);
43
- console.log(' npm install');
44
- console.log(' npx @fluffjs/cli serve');
39
+ if (options.packageManager) {
40
+ console.log('');
41
+ console.log(` Installing dependencies with ${options.packageManager}...`);
42
+ execSync(`${options.packageManager} install`, { cwd: appDir, stdio: 'inherit' });
43
+ console.log('');
44
+ console.log(' App created successfully!');
45
+ console.log('');
46
+ console.log('Next steps:');
47
+ console.log(` cd ${kebabName}`);
48
+ console.log(' npx @fluffjs/cli serve');
49
+ }
50
+ else {
51
+ console.log('');
52
+ console.log('✅ App created successfully!');
53
+ console.log('');
54
+ console.log('Next steps:');
55
+ console.log(` cd ${kebabName}`);
56
+ console.log(' npm install');
57
+ console.log(' npx @fluffjs/cli serve');
58
+ }
45
59
  }
46
60
  writeFile(filePath, content) {
47
61
  if (fs.existsSync(filePath)) {
@@ -6,6 +6,7 @@ import * as t from '@babel/types';
6
6
  import { generate } from './BabelHelpers.js';
7
7
  import { CodeGenerator } from './CodeGenerator.js';
8
8
  import { ComponentCompiler } from './ComponentCompiler.js';
9
+ import { DecoratorValidator } from './DecoratorValidator.js';
9
10
  const VIRTUAL_EXPR_TABLE_ID = '@fluff/expr-table';
10
11
  function findFluffSourcePath() {
11
12
  const thisFile = fileURLToPath(import.meta.url);
@@ -121,6 +122,7 @@ export function fluffPlugin(options) {
121
122
  }
122
123
  if (decorators.hasPipe) {
123
124
  const transformed = await compiler.transformPipeDecorators(source, args.path);
125
+ DecoratorValidator.validate(transformed, args.path);
124
126
  return {
125
127
  contents: transformed,
126
128
  loader: 'ts',
package/index.d.ts CHANGED
@@ -2,6 +2,8 @@ export { ComponentCompiler } from './ComponentCompiler.js';
2
2
  export { TemplateParser } from './TemplateParser.js';
3
3
  export { CodeGenerator } from './CodeGenerator.js';
4
4
  export { Cli } from './Cli.js';
5
+ export { Generator } from './Generator.js';
6
+ export type { PackageManager } from './interfaces/GeneratorOptions.js';
5
7
  export type { ComponentInfo } from './interfaces/ComponentInfo.js';
6
8
  export type { CompilerOptions } from './interfaces/CompilerOptions.js';
7
9
  export type { ControlFlow, SwitchCaseOld } from './interfaces/ControlFlow.js';
package/index.js CHANGED
@@ -2,3 +2,4 @@ export { ComponentCompiler } from './ComponentCompiler.js';
2
2
  export { TemplateParser } from './TemplateParser.js';
3
3
  export { CodeGenerator } from './CodeGenerator.js';
4
4
  export { Cli } from './Cli.js';
5
+ export { Generator } from './Generator.js';
@@ -1,5 +1,7 @@
1
+ export type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';
1
2
  export interface GeneratorOptions {
2
3
  appName: string;
3
4
  outputDir: string;
5
+ packageManager?: PackageManager;
4
6
  }
5
7
  //# sourceMappingURL=GeneratorOptions.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluffjs/cli",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",