@fluffjs/cli 0.0.8 → 0.1.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 (142) hide show
  1. package/BabelHelpers.d.ts +26 -0
  2. package/BabelHelpers.js +65 -0
  3. package/Cli.d.ts +7 -10
  4. package/Cli.js +139 -52
  5. package/CodeGenerator.d.ts +53 -39
  6. package/CodeGenerator.js +330 -725
  7. package/ComponentCompiler.d.ts +14 -16
  8. package/ComponentCompiler.js +193 -257
  9. package/DomPreProcessor.d.ts +36 -0
  10. package/DomPreProcessor.js +645 -0
  11. package/ErrorHelpers.d.ts +5 -0
  12. package/ErrorHelpers.js +8 -0
  13. package/ExpressionTransformer.d.ts +38 -28
  14. package/ExpressionTransformer.js +558 -230
  15. package/Generator.d.ts +1 -5
  16. package/Generator.js +128 -67
  17. package/GetterDependencyExtractor.d.ts +4 -0
  18. package/GetterDependencyExtractor.js +73 -0
  19. package/IndexHtmlTransformer.d.ts +6 -7
  20. package/IndexHtmlTransformer.js +82 -88
  21. package/Parse5Helpers.d.ts +17 -0
  22. package/Parse5Helpers.js +95 -0
  23. package/TemplateParser.d.ts +39 -21
  24. package/TemplateParser.js +462 -268
  25. package/Typeguards.d.ts +24 -0
  26. package/Typeguards.js +30 -0
  27. package/babel-plugin-class-transform.d.ts +3 -18
  28. package/babel-plugin-class-transform.js +3 -11
  29. package/babel-plugin-component.d.ts +4 -13
  30. package/babel-plugin-component.js +7 -0
  31. package/babel-plugin-imports.d.ts +3 -11
  32. package/babel-plugin-imports.js +5 -31
  33. package/babel-plugin-reactive.d.ts +2 -19
  34. package/babel-plugin-reactive.js +21 -76
  35. package/bin.js +2 -2
  36. package/fluff-esbuild-plugin.d.ts +2 -5
  37. package/fluff-esbuild-plugin.js +4 -1
  38. package/index.d.ts +6 -2
  39. package/index.js +1 -1
  40. package/interfaces/BabelPluginClassTransformState.d.ts +5 -0
  41. package/interfaces/BabelPluginComponentState.d.ts +4 -0
  42. package/interfaces/BabelPluginComponentState.js +1 -0
  43. package/interfaces/BabelPluginImportsState.d.ts +5 -0
  44. package/interfaces/BabelPluginImportsState.js +1 -0
  45. package/interfaces/BabelPluginReactiveState.d.ts +13 -0
  46. package/interfaces/BabelPluginReactiveState.js +1 -0
  47. package/interfaces/BabelPluginReactiveWatchCallInfo.d.ts +7 -0
  48. package/interfaces/BabelPluginReactiveWatchCallInfo.js +1 -0
  49. package/interfaces/BabelPluginReactiveWatchInfo.d.ts +5 -0
  50. package/interfaces/BabelPluginReactiveWatchInfo.js +1 -0
  51. package/interfaces/BabelToken.d.ts +8 -0
  52. package/interfaces/BabelToken.js +1 -0
  53. package/interfaces/BindingInfo.d.ts +12 -0
  54. package/interfaces/BindingInfo.js +1 -0
  55. package/interfaces/BreakMarkerConfig.d.ts +4 -0
  56. package/interfaces/BreakMarkerConfig.js +1 -0
  57. package/interfaces/BreakNode.d.ts +4 -0
  58. package/interfaces/BreakNode.js +1 -0
  59. package/interfaces/BundleOptions.d.ts +9 -0
  60. package/interfaces/BundleOptions.js +1 -0
  61. package/interfaces/ClassTransformOptions.d.ts +10 -0
  62. package/interfaces/ClassTransformOptions.js +1 -0
  63. package/interfaces/CliOptions.d.ts +8 -0
  64. package/interfaces/CliOptions.js +1 -0
  65. package/interfaces/CommentNode.d.ts +5 -0
  66. package/interfaces/CommentNode.js +1 -0
  67. package/interfaces/CompileResult.d.ts +6 -0
  68. package/interfaces/CompileResult.js +1 -0
  69. package/interfaces/CompilerOptions.d.ts +6 -0
  70. package/interfaces/CompilerOptions.js +1 -0
  71. package/interfaces/ComponentInfo.d.ts +8 -0
  72. package/interfaces/ComponentInfo.js +1 -0
  73. package/interfaces/ComponentMetadata.d.ts +9 -0
  74. package/interfaces/ComponentMetadata.js +1 -0
  75. package/interfaces/ControlFlow.d.ts +19 -0
  76. package/interfaces/ControlFlow.js +1 -0
  77. package/interfaces/ControlFlowNode.d.ts +6 -0
  78. package/interfaces/ControlFlowNode.js +1 -0
  79. package/interfaces/ControlFlowParseResult.d.ts +10 -0
  80. package/interfaces/ControlFlowParseResult.js +1 -0
  81. package/interfaces/ElementNode.d.ts +11 -0
  82. package/interfaces/ElementNode.js +1 -0
  83. package/interfaces/FluffConfigInterface.d.ts +7 -0
  84. package/interfaces/FluffConfigInterface.js +1 -0
  85. package/interfaces/FluffPluginOptions.d.ts +9 -0
  86. package/interfaces/FluffPluginOptions.js +1 -0
  87. package/interfaces/FluffTarget.d.ts +15 -0
  88. package/interfaces/FluffTarget.js +1 -0
  89. package/interfaces/ForMarkerConfig.d.ts +9 -0
  90. package/interfaces/ForMarkerConfig.js +1 -0
  91. package/interfaces/ForNode.d.ts +13 -0
  92. package/interfaces/ForNode.js +1 -0
  93. package/interfaces/GeneratorOptions.d.ts +5 -0
  94. package/interfaces/GeneratorOptions.js +1 -0
  95. package/interfaces/HtmlTransformOptions.d.ts +10 -0
  96. package/interfaces/HtmlTransformOptions.js +1 -0
  97. package/interfaces/IfBranch.d.ts +8 -0
  98. package/interfaces/IfBranch.js +1 -0
  99. package/interfaces/IfMarkerConfig.d.ts +8 -0
  100. package/interfaces/IfMarkerConfig.js +1 -0
  101. package/interfaces/IfNode.d.ts +7 -0
  102. package/interfaces/IfNode.js +1 -0
  103. package/interfaces/ImportTransformOptions.d.ts +7 -0
  104. package/interfaces/ImportTransformOptions.js +1 -0
  105. package/interfaces/InterpolationNode.d.ts +12 -0
  106. package/interfaces/InterpolationNode.js +1 -0
  107. package/interfaces/ParsedTemplate.d.ts +6 -0
  108. package/interfaces/ParsedTemplate.js +1 -0
  109. package/interfaces/ParsedTemplateOld.d.ts +9 -0
  110. package/interfaces/ParsedTemplateOld.js +1 -0
  111. package/interfaces/PropertyChain.d.ts +2 -0
  112. package/interfaces/PropertyChain.js +1 -0
  113. package/interfaces/Scope.d.ts +5 -0
  114. package/interfaces/Scope.js +1 -0
  115. package/interfaces/ServeOptions.d.ts +5 -0
  116. package/interfaces/ServeOptions.js +1 -0
  117. package/interfaces/SwitchCase.d.ts +8 -0
  118. package/interfaces/SwitchCase.js +1 -0
  119. package/interfaces/SwitchMarkerConfig.d.ts +11 -0
  120. package/interfaces/SwitchMarkerConfig.js +1 -0
  121. package/interfaces/SwitchNode.d.ts +10 -0
  122. package/interfaces/SwitchNode.js +1 -0
  123. package/interfaces/TemplateBinding.d.ts +10 -0
  124. package/interfaces/TemplateBinding.js +1 -0
  125. package/interfaces/TemplateNode.d.ts +7 -0
  126. package/interfaces/TemplateNode.js +1 -0
  127. package/interfaces/TextMarkerConfig.d.ts +10 -0
  128. package/interfaces/TextMarkerConfig.js +1 -0
  129. package/interfaces/TextNode.d.ts +5 -0
  130. package/interfaces/TextNode.js +1 -0
  131. package/interfaces/TokenizeResult.d.ts +6 -0
  132. package/interfaces/TokenizeResult.js +1 -0
  133. package/interfaces/TransformOptions.d.ts +11 -0
  134. package/interfaces/TransformOptions.js +1 -0
  135. package/interfaces/index.d.ts +34 -0
  136. package/interfaces/index.js +1 -0
  137. package/package.json +9 -1
  138. package/types/FluffConfig.d.ts +5 -27
  139. package/ControlFlowParser.d.ts +0 -55
  140. package/ControlFlowParser.js +0 -279
  141. package/types.d.ts +0 -46
  142. /package/{types.js → interfaces/BabelPluginClassTransformState.js} +0 -0
@@ -1,28 +1,26 @@
1
1
  import type { ClassTransformOptions } from './babel-plugin-class-transform.js';
2
2
  import type { ComponentMetadata } from './babel-plugin-component.js';
3
- export interface CompileResult {
4
- code: string;
5
- map?: string;
6
- watchFiles?: string[];
7
- }
3
+ import type { CompileResult } from './interfaces/CompileResult.js';
4
+ import { TemplateParser } from './TemplateParser.js';
5
+ export type { CompileResult } from './interfaces/CompileResult.js';
8
6
  export declare class ComponentCompiler {
9
7
  private readonly componentSelectors;
10
- private readonly parser;
11
- private readonly generator;
12
- constructor();
13
- registerComponent(selector: string): void;
14
- discoverComponents(demoDir: string): Promise<void>;
15
- compileComponent(filePath: string): Promise<string>;
16
- compileComponentForBundle(filePath: string, minify?: boolean, sourcemap?: boolean): Promise<CompileResult>;
8
+ private getReactivePropsForFile;
9
+ protected createTemplateParser(_filePath: string): TemplateParser;
10
+ private runBabelTransform;
11
+ discoverComponents(dir: string): Promise<void>;
12
+ compileComponentForBundle(filePath: string, minify?: boolean, sourcemap?: boolean, skipDefine?: boolean, production?: boolean): Promise<CompileResult>;
17
13
  stripTypeScriptWithSourceMap(code: string, filePath: string, sourcemap?: boolean): Promise<CompileResult>;
18
- private createComponentSourceMap;
19
14
  transformImportsForBundle(code: string, filePath: string): Promise<string>;
20
- transformReactiveProperties(code: string, filePath?: string): Promise<string>;
15
+ transformReactiveProperties(code: string, filePath?: string, production?: boolean): Promise<string>;
21
16
  stripTypeScript(code: string, filePath?: string): Promise<string>;
22
17
  extractComponentMetadata(code: string, filePath: string): Promise<ComponentMetadata | null>;
23
- transformImportsAndDecorators(code: string, filePath: string): Promise<string>;
24
18
  transformClass(code: string, filePath: string, options: ClassTransformOptions): Promise<string>;
25
19
  transformLibraryImports(code: string, filePath: string): Promise<string>;
26
- copyLighterLib(srcDir: string, distDir: string): Promise<void>;
20
+ private createComponentSourceMap;
21
+ private addFluffImport;
22
+ private appendCode;
23
+ private addBindingsMap;
24
+ private addCustomElementsDefine;
27
25
  }
28
26
  //# sourceMappingURL=ComponentCompiler.d.ts.map
@@ -1,105 +1,88 @@
1
1
  import * as babel from '@babel/core';
2
+ import { parse } from '@babel/parser';
3
+ import * as t from '@babel/types';
2
4
  import * as esbuild from 'esbuild';
3
5
  import * as fs from 'fs';
6
+ import * as parse5 from 'parse5';
4
7
  import { minify as minifyHtml } from 'html-minifier-terser';
5
8
  import * as path from 'path';
6
9
  import { SourceMapConsumer, SourceMapGenerator } from 'source-map';
7
- import classTransformPlugin, { injectMethodBodies } from './babel-plugin-class-transform.js';
10
+ import classTransformPlugin from './babel-plugin-class-transform.js';
8
11
  import componentPlugin, { componentMetadataMap } from './babel-plugin-component.js';
9
12
  import importsPlugin from './babel-plugin-imports.js';
10
13
  import reactivePlugin, { reactivePropertiesMap } from './babel-plugin-reactive.js';
14
+ import { generate } from './BabelHelpers.js';
11
15
  import { CodeGenerator } from './CodeGenerator.js';
16
+ import { ErrorHelpers } from './ErrorHelpers.js';
17
+ import { GetterDependencyExtractor } from './GetterDependencyExtractor.js';
18
+ import { Parse5Helpers } from './Parse5Helpers.js';
12
19
  import { TemplateParser } from './TemplateParser.js';
13
20
  export class ComponentCompiler {
14
21
  componentSelectors = new Set();
15
- parser;
16
- generator;
17
- constructor() {
18
- this.parser = new TemplateParser();
19
- this.generator = new CodeGenerator();
20
- }
21
- registerComponent(selector) {
22
- this.componentSelectors.add(selector);
23
- }
24
- async discoverComponents(demoDir) {
25
- const files = fs.readdirSync(demoDir)
26
- .filter(f => f.endsWith('.component.ts'));
27
- for (const file of files) {
28
- const filePath = path.join(demoDir, file);
29
- const content = fs.readFileSync(filePath, 'utf-8');
30
- const metadata = await this.extractComponentMetadata(content, filePath);
31
- if (metadata?.selector) {
32
- this.componentSelectors.add(metadata.selector);
22
+ getReactivePropsForFile(filePath) {
23
+ const direct = reactivePropertiesMap.get(filePath);
24
+ if (direct) {
25
+ return direct;
26
+ }
27
+ for (const [key, value] of reactivePropertiesMap.entries()) {
28
+ if (key === filePath || key.endsWith(filePath) || filePath.endsWith(key)) {
29
+ return value;
33
30
  }
34
31
  }
32
+ return new Set();
35
33
  }
36
- async compileComponent(filePath) {
37
- let source = fs.readFileSync(filePath, 'utf-8');
38
- const componentDir = path.dirname(filePath);
39
- if (source.includes('@Reactive') || source.includes('@Input')) {
40
- source = await this.transformReactiveProperties(source, filePath);
41
- const reactiveProps = reactivePropertiesMap.get(filePath);
42
- if (reactiveProps) {
43
- this.generator.setReactiveProperties(reactiveProps);
44
- }
45
- }
46
- else {
47
- this.generator.setReactiveProperties(new Set());
48
- }
49
- const metadata = await this.extractComponentMetadata(source, filePath);
50
- if (!metadata) {
51
- return source;
52
- }
53
- const { selector, templateUrl, template, styleUrl, styles: inlineStyles, className } = metadata;
54
- let templateHtml = '';
55
- if (templateUrl) {
56
- const templatePath = path.resolve(componentDir, templateUrl);
57
- templateHtml = fs.readFileSync(templatePath, 'utf-8');
58
- }
59
- else if (template) {
60
- templateHtml = template;
34
+ createTemplateParser(_filePath) {
35
+ return new TemplateParser();
36
+ }
37
+ async runBabelTransform(code, filePath, options) {
38
+ try {
39
+ const presets = options.useTypeScriptPreset
40
+ ? [['@babel/preset-typescript', { isTSX: false, allExtensions: true }]]
41
+ : [];
42
+ const plugins = options.useDecoratorSyntax
43
+ ? [['@babel/plugin-syntax-decorators', { version: '2023-11' }], ...options.plugins]
44
+ : options.plugins;
45
+ const parserOpts = options.useDecoratorSyntax
46
+ ? { plugins: ['typescript', 'decorators'] }
47
+ : options.useTypeScriptPreset
48
+ ? { plugins: ['typescript'] }
49
+ : undefined;
50
+ const result = await babel.transformAsync(code, {
51
+ filename: filePath,
52
+ presets,
53
+ plugins,
54
+ parserOpts
55
+ });
56
+ return result?.code ?? code;
61
57
  }
62
- else {
63
- return source;
58
+ catch (e) {
59
+ console.error(`${options.errorContext} in ${filePath}:`, ErrorHelpers.getErrorMessage(e));
60
+ return code;
64
61
  }
65
- let styles = '';
66
- if (styleUrl) {
67
- const stylePath = path.resolve(componentDir, styleUrl);
68
- if (fs.existsSync(stylePath)) {
69
- styles = fs.readFileSync(stylePath, 'utf-8');
62
+ }
63
+ async discoverComponents(dir) {
64
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
65
+ for (const entry of entries) {
66
+ const fullPath = path.join(dir, entry.name);
67
+ if (entry.isDirectory()) {
68
+ await this.discoverComponents(fullPath);
69
+ }
70
+ else if (entry.name.endsWith('.component.ts')) {
71
+ const content = fs.readFileSync(fullPath, 'utf-8');
72
+ const metadata = await this.extractComponentMetadata(content, fullPath);
73
+ if (metadata?.selector) {
74
+ this.componentSelectors.add(metadata.selector);
75
+ }
70
76
  }
71
77
  }
72
- else if (inlineStyles) {
73
- styles = inlineStyles;
74
- }
75
- const { html, bindings, controlFlows, templateRefs } = this.parser.parse(templateHtml);
76
- this.generator.setTemplateRefs(templateRefs);
77
- const renderMethod = this.generator.generateRenderMethod(html, styles);
78
- const bindingsSetup = this.generator.generateBindingsSetup(bindings, controlFlows);
79
- let result = await this.transformImportsAndDecorators(source, filePath);
80
- result = await this.transformClass(result, filePath, {
81
- className, originalSuperClass: 'HTMLElement', newSuperClass: 'FluffElement', injectMethods: [
82
- { name: '__render', body: renderMethod }, { name: '__setupBindings', body: bindingsSetup }
83
- ]
84
- });
85
- result = 'import { FluffElement } from \'./fluff-lib/runtime/FluffElement.js\';\n' + result;
86
- result += `\ncustomElements.define('${selector}', ${className});\n`;
87
- result = await this.stripTypeScript(result, filePath);
88
- return result;
89
78
  }
90
- async compileComponentForBundle(filePath, minify, sourcemap) {
79
+ async compileComponentForBundle(filePath, minify, sourcemap, skipDefine, production) {
91
80
  let source = fs.readFileSync(filePath, 'utf-8');
92
81
  const componentDir = path.dirname(filePath);
93
- const generator = new CodeGenerator();
82
+ const parser = this.createTemplateParser(filePath);
83
+ reactivePropertiesMap.delete(filePath);
94
84
  if (source.includes('@Reactive') || source.includes('@Input')) {
95
- source = await this.transformReactiveProperties(source, filePath);
96
- const reactiveProps = reactivePropertiesMap.get(filePath);
97
- if (reactiveProps) {
98
- generator.setReactiveProperties(reactiveProps);
99
- }
100
- }
101
- else {
102
- generator.setReactiveProperties(new Set());
85
+ source = await this.transformReactiveProperties(source, filePath, production);
103
86
  }
104
87
  const metadata = await this.extractComponentMetadata(source, filePath);
105
88
  if (!metadata) {
@@ -126,32 +109,51 @@ export class ComponentCompiler {
126
109
  else if (inlineStyles) {
127
110
  styles = inlineStyles;
128
111
  }
112
+ if (minify && styles) {
113
+ const cssResult = await esbuild.transform(styles, {
114
+ loader: 'css', minify: true
115
+ });
116
+ styles = cssResult.code;
117
+ }
118
+ const reactiveProps = this.getReactivePropsForFile(filePath);
119
+ const getterDepMap = GetterDependencyExtractor.extractGetterDependencyMap(source, reactiveProps);
120
+ parser.setGetterDependencyMap(getterDepMap);
121
+ const parsed = await parser.parse(templateHtml);
122
+ parser.setGetterDependencyMap(new Map());
123
+ const gen = new CodeGenerator(this.componentSelectors, selector);
124
+ let generatedHtml = gen.generateHtml(parsed);
129
125
  if (minify) {
130
- templateHtml = await minifyHtml(templateHtml, {
126
+ const fragment = parse5.parseFragment(generatedHtml);
127
+ Parse5Helpers.removeNonMarkerComments(fragment);
128
+ generatedHtml = parse5.serialize(fragment);
129
+ generatedHtml = await minifyHtml(generatedHtml, {
131
130
  collapseWhitespace: true,
132
- removeComments: true,
131
+ removeComments: false,
133
132
  removeRedundantAttributes: true,
134
133
  removeEmptyAttributes: true
135
134
  });
136
- if (styles) {
137
- const cssResult = await esbuild.transform(styles, {
138
- loader: 'css', minify: true
139
- });
140
- styles = cssResult.code;
141
- }
142
135
  }
143
- const { html, bindings, controlFlows, templateRefs } = this.parser.parse(templateHtml);
144
- generator.setTemplateRefs(templateRefs);
145
- const renderMethod = generator.generateRenderMethod(html, styles);
146
- const bindingsSetup = generator.generateBindingsSetup(bindings, controlFlows);
136
+ const markerConfigJson = gen.getMarkerConfigJson();
137
+ const renderMethod = gen.generateRenderMethodFromHtml(generatedHtml, styles, markerConfigJson);
138
+ const bindingsSetup = gen.generateBindingsSetup();
147
139
  let result = await this.transformImportsForBundle(source, filePath);
148
140
  result = await this.transformClass(result, filePath, {
149
141
  className, originalSuperClass: 'HTMLElement', newSuperClass: 'FluffElement', injectMethods: [
150
142
  { name: '__render', body: renderMethod }, { name: '__setupBindings', body: bindingsSetup }
151
143
  ]
152
144
  });
153
- result = 'import { FluffElement } from \'@fluffjs/fluff\';\n' + result;
154
- result += `\ncustomElements.define('${selector}', ${className});\n`;
145
+ result = this.addFluffImport(result);
146
+ const exprAssignments = gen.generateExpressionAssignments();
147
+ if (exprAssignments) {
148
+ result = this.appendCode(result, exprAssignments);
149
+ }
150
+ const bindingsMap = gen.getBindingsMap();
151
+ if (Object.keys(bindingsMap).length > 0) {
152
+ result = this.addBindingsMap(result, className, bindingsMap);
153
+ }
154
+ if (!skipDefine) {
155
+ result = this.addCustomElementsDefine(result, selector, className);
156
+ }
155
157
  const tsResult = await this.stripTypeScriptWithSourceMap(result, filePath, sourcemap);
156
158
  const watchFiles = [];
157
159
  if (templatePath) {
@@ -178,78 +180,31 @@ export class ComponentCompiler {
178
180
  return { code: result.code, map: result.map };
179
181
  }
180
182
  catch (e) {
181
- const message = e instanceof Error ? e.message : String(e);
182
- console.error(`Error transforming ${filePath}:`, message);
183
+ console.error(`Error transforming ${filePath}:`, ErrorHelpers.getErrorMessage(e));
183
184
  return { code };
184
185
  }
185
186
  }
186
- async createComponentSourceMap(code, esbuildMap, componentPath, templatePath, stylePath) {
187
- const consumer = await new SourceMapConsumer(JSON.parse(esbuildMap));
188
- const generator = new SourceMapGenerator({
189
- file: path.basename(componentPath)
190
- .replace('.ts', '.js')
191
- });
192
- generator.setSourceContent(componentPath, fs.readFileSync(componentPath, 'utf-8'));
193
- generator.setSourceContent(templatePath, fs.readFileSync(templatePath, 'utf-8'));
194
- if (stylePath && fs.existsSync(stylePath)) {
195
- generator.setSourceContent(stylePath, fs.readFileSync(stylePath, 'utf-8'));
196
- }
197
- consumer.eachMapping(mapping => {
198
- if (mapping.source) {
199
- generator.addMapping({
200
- generated: { line: mapping.generatedLine, column: mapping.generatedColumn },
201
- original: { line: mapping.originalLine, column: mapping.originalColumn },
202
- source: mapping.source,
203
- name: mapping.name ?? undefined
204
- });
205
- }
206
- });
207
- consumer.destroy();
208
- return generator.toString();
209
- }
210
187
  async transformImportsForBundle(code, filePath) {
211
- try {
212
- const importOptions = {
213
- removeImportsFrom: ['lighter'],
214
- removeDecorators: ['Component', 'Input', 'Output'],
215
- pathReplacements: {},
216
- addJsExtension: false
217
- };
218
- const result = await babel.transformAsync(code, {
219
- filename: filePath, presets: [
220
- ['@babel/preset-typescript', { isTSX: false, allExtensions: true }]
221
- ], plugins: [
222
- ['@babel/plugin-syntax-decorators', { version: '2023-11' }], [importsPlugin, importOptions]
223
- ], parserOpts: {
224
- plugins: ['typescript', 'decorators']
225
- }
226
- });
227
- return result?.code ?? code;
228
- }
229
- catch (e) {
230
- const message = e instanceof Error ? e.message : String(e);
231
- console.error(`Failed to transform imports in ${filePath}:`, message);
232
- return code;
233
- }
188
+ const importOptions = {
189
+ removeImportsFrom: ['lighter'],
190
+ removeDecorators: ['Component', 'Input', 'Output'],
191
+ pathReplacements: {},
192
+ addJsExtension: false
193
+ };
194
+ return this.runBabelTransform(code, filePath, {
195
+ useTypeScriptPreset: true,
196
+ useDecoratorSyntax: true,
197
+ plugins: [[importsPlugin, importOptions]],
198
+ errorContext: 'Failed to transform imports'
199
+ });
234
200
  }
235
- async transformReactiveProperties(code, filePath = 'file.ts') {
236
- try {
237
- const result = await babel.transformAsync(code, {
238
- filename: filePath, presets: [
239
- ['@babel/preset-typescript', { isTSX: false, allExtensions: true }]
240
- ], plugins: [
241
- ['@babel/plugin-syntax-decorators', { version: '2023-11' }], reactivePlugin
242
- ], parserOpts: {
243
- plugins: ['typescript', 'decorators']
244
- }
245
- });
246
- return result?.code ?? code;
247
- }
248
- catch (e) {
249
- const message = e instanceof Error ? e.message : String(e);
250
- console.error(`Babel transform error in ${filePath}:`, message);
251
- return code;
252
- }
201
+ async transformReactiveProperties(code, filePath = 'file.ts', production) {
202
+ return this.runBabelTransform(code, filePath, {
203
+ useTypeScriptPreset: true,
204
+ useDecoratorSyntax: true,
205
+ plugins: [[reactivePlugin, { production: production ?? false }]],
206
+ errorContext: 'Babel transform error'
207
+ });
253
208
  }
254
209
  async stripTypeScript(code, filePath = 'file.ts') {
255
210
  try {
@@ -259,123 +214,104 @@ export class ComponentCompiler {
259
214
  return result.code;
260
215
  }
261
216
  catch (e) {
262
- const message = e instanceof Error ? e.message : String(e);
263
- console.error(`Error transforming ${filePath}:`, message);
217
+ console.error(`Error transforming ${filePath}:`, ErrorHelpers.getErrorMessage(e));
264
218
  return code;
265
219
  }
266
220
  }
267
221
  async extractComponentMetadata(code, filePath) {
268
222
  try {
269
223
  componentMetadataMap.delete(filePath);
270
- await babel.transformAsync(code, {
271
- filename: filePath, presets: [
272
- ['@babel/preset-typescript', { isTSX: false, allExtensions: true }]
273
- ], plugins: [
274
- ['@babel/plugin-syntax-decorators', { version: '2023-11' }], componentPlugin
275
- ], parserOpts: {
276
- plugins: ['typescript', 'decorators']
277
- }
224
+ await this.runBabelTransform(code, filePath, {
225
+ useTypeScriptPreset: true,
226
+ useDecoratorSyntax: true,
227
+ plugins: [componentPlugin],
228
+ errorContext: 'Failed to extract component metadata'
278
229
  });
279
230
  return componentMetadataMap.get(filePath) ?? null;
280
231
  }
281
232
  catch (e) {
282
- const message = e instanceof Error ? e.message : String(e);
283
- console.error(`Failed to extract component metadata from ${filePath}:`, message);
233
+ console.error(`Failed to extract component metadata from ${filePath}:`, ErrorHelpers.getErrorMessage(e));
284
234
  return null;
285
235
  }
286
236
  }
287
- async transformImportsAndDecorators(code, filePath) {
288
- try {
289
- const importOptions = {
290
- removeImportsFrom: ['lighter'], removeDecorators: ['Component', 'Input', 'Output'], pathReplacements: {
291
- '@/fluff-lib/': './fluff-lib/'
292
- }, addJsExtension: true
293
- };
294
- const result = await babel.transformAsync(code, {
295
- filename: filePath, presets: [
296
- ['@babel/preset-typescript', { isTSX: false, allExtensions: true }]
297
- ], plugins: [
298
- ['@babel/plugin-syntax-decorators', { version: '2023-11' }], [importsPlugin, importOptions]
299
- ], parserOpts: {
300
- plugins: ['typescript', 'decorators']
301
- }
302
- });
303
- return result?.code ?? code;
304
- }
305
- catch (e) {
306
- const message = e instanceof Error ? e.message : String(e);
307
- console.error(`Failed to transform imports in ${filePath}:`, message);
308
- return code;
309
- }
310
- }
311
237
  async transformClass(code, filePath, options) {
312
- try {
313
- const result = await babel.transformAsync(code, {
314
- filename: filePath, plugins: [
315
- [classTransformPlugin, options]
316
- ]
317
- });
318
- let transformed = result?.code ?? code;
319
- if (options.injectMethods) {
320
- transformed = injectMethodBodies(transformed, options.injectMethods);
321
- }
322
- return transformed;
323
- }
324
- catch (e) {
325
- const message = e instanceof Error ? e.message : String(e);
326
- console.error(`Failed to transform class in ${filePath}:`, message);
327
- return code;
328
- }
238
+ return this.runBabelTransform(code, filePath, {
239
+ useTypeScriptPreset: false,
240
+ useDecoratorSyntax: false,
241
+ plugins: [[classTransformPlugin, options]],
242
+ errorContext: 'Failed to transform class'
243
+ });
329
244
  }
330
245
  async transformLibraryImports(code, filePath) {
331
- try {
332
- const importOptions = {
333
- pathReplacements: {
334
- '@/fluff-lib/': '../'
335
- }, addJsExtension: true
336
- };
337
- const result = await babel.transformAsync(code, {
338
- filename: filePath, presets: [
339
- ['@babel/preset-typescript', { isTSX: false, allExtensions: true }]
340
- ], plugins: [
341
- [importsPlugin, importOptions]
342
- ], parserOpts: {
343
- plugins: ['typescript']
344
- }
345
- });
346
- return result?.code ?? code;
347
- }
348
- catch (e) {
349
- const message = e instanceof Error ? e.message : String(e);
350
- console.error(`Failed to transform library imports in ${filePath}:`, message);
351
- return code;
352
- }
246
+ const importOptions = {
247
+ pathReplacements: {
248
+ '@/fluff-lib/': '../'
249
+ }, addJsExtension: true
250
+ };
251
+ return this.runBabelTransform(code, filePath, {
252
+ useTypeScriptPreset: true,
253
+ useDecoratorSyntax: false,
254
+ plugins: [[importsPlugin, importOptions]],
255
+ errorContext: 'Failed to transform library imports'
256
+ });
353
257
  }
354
- async copyLighterLib(srcDir, distDir) {
355
- const lighterLibSrc = path.join(srcDir, 'fluff-lib');
356
- const lighterLibDist = path.join(distDir, 'fluff-lib');
357
- if (!fs.existsSync(lighterLibDist)) {
358
- fs.mkdirSync(lighterLibDist, { recursive: true });
258
+ async createComponentSourceMap(code, esbuildMap, componentPath, templatePath, stylePath) {
259
+ const consumer = await new SourceMapConsumer(JSON.parse(esbuildMap));
260
+ const generator = new SourceMapGenerator({
261
+ file: path.basename(componentPath)
262
+ .replace('.ts', '.js')
263
+ });
264
+ generator.setSourceContent(componentPath, fs.readFileSync(componentPath, 'utf-8'));
265
+ generator.setSourceContent(templatePath, fs.readFileSync(templatePath, 'utf-8'));
266
+ if (stylePath && fs.existsSync(stylePath)) {
267
+ generator.setSourceContent(stylePath, fs.readFileSync(stylePath, 'utf-8'));
359
268
  }
360
- const dirs = ['utils', 'runtime', 'interfaces', 'enums', 'decorators'];
361
- for (const dir of dirs) {
362
- const srcPath = path.join(lighterLibSrc, dir);
363
- const distPath = path.join(lighterLibDist, dir);
364
- if (fs.existsSync(srcPath)) {
365
- if (!fs.existsSync(distPath)) {
366
- fs.mkdirSync(distPath, { recursive: true });
367
- }
368
- const files = fs.readdirSync(srcPath)
369
- .filter(f => f.endsWith('.ts'));
370
- for (const file of files) {
371
- const fullPath = path.join(srcPath, file);
372
- let content = fs.readFileSync(fullPath, 'utf-8');
373
- content = await this.transformLibraryImports(content, fullPath);
374
- content = await this.stripTypeScript(content, fullPath);
375
- const outFile = file.replace('.ts', '.js');
376
- fs.writeFileSync(path.join(distPath, outFile), content);
377
- }
269
+ consumer.eachMapping(mapping => {
270
+ if (mapping.source) {
271
+ generator.addMapping({
272
+ generated: { line: mapping.generatedLine, column: mapping.generatedColumn },
273
+ original: { line: mapping.originalLine, column: mapping.originalColumn },
274
+ source: mapping.source,
275
+ name: mapping.name ?? undefined
276
+ });
378
277
  }
278
+ });
279
+ consumer.destroy();
280
+ return generator.toString();
281
+ }
282
+ addFluffImport(code) {
283
+ const ast = parse(code, { sourceType: 'module' });
284
+ const importSpecifiers = [
285
+ t.importSpecifier(t.identifier('FluffBase'), t.identifier('FluffBase')),
286
+ t.importSpecifier(t.identifier('FluffElement'), t.identifier('FluffElement')),
287
+ t.importSpecifier(t.identifier('MarkerManager'), t.identifier('MarkerManager'))
288
+ ];
289
+ const importDecl = t.importDeclaration(importSpecifiers, t.stringLiteral('@fluffjs/fluff'));
290
+ ast.program.body.unshift(importDecl);
291
+ return generate(ast, { compact: false }).code;
292
+ }
293
+ appendCode(code, additionalCode) {
294
+ const ast = parse(code, { sourceType: 'module' });
295
+ const additionalAst = parse(additionalCode, { sourceType: 'module' });
296
+ ast.program.body.push(...additionalAst.program.body);
297
+ return generate(ast, { compact: false }).code;
298
+ }
299
+ addBindingsMap(code, className, bindingsMap) {
300
+ const ast = parse(code, { sourceType: 'module' });
301
+ const jsonStr = JSON.stringify(bindingsMap);
302
+ const valueAst = parse(`(${jsonStr})`, { sourceType: 'module' });
303
+ const [valueStmt] = valueAst.program.body;
304
+ if (!t.isExpressionStatement(valueStmt)) {
305
+ return code;
379
306
  }
307
+ const assignment = t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier(className), t.identifier('__bindings')), valueStmt.expression));
308
+ ast.program.body.push(assignment);
309
+ return generate(ast, { compact: false }).code;
310
+ }
311
+ addCustomElementsDefine(code, selector, className) {
312
+ const ast = parse(code, { sourceType: 'module' });
313
+ const defineCall = t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('customElements'), t.identifier('define')), [t.stringLiteral(selector), t.identifier(className)]));
314
+ ast.program.body.push(defineCall);
315
+ return generate(ast, { compact: false }).code;
380
316
  }
381
317
  }
@@ -0,0 +1,36 @@
1
+ export declare class DomPreProcessor {
2
+ private readonly stack;
3
+ private source;
4
+ private rootFragment;
5
+ private readonly elementStack;
6
+ process(html: string): Promise<string>;
7
+ private handleStartTag;
8
+ private transformAttribute;
9
+ private convertInterpolationToExpression;
10
+ private handleEndTag;
11
+ private handleText;
12
+ private appendNode;
13
+ private appendText;
14
+ private appendDoctype;
15
+ private findNextSpecial;
16
+ private parseInterpolation;
17
+ private parseControlFlow;
18
+ private parseExpressionBlockCore;
19
+ private parseSimpleBlockCore;
20
+ private parseIfStatement;
21
+ private parseElseIfStatement;
22
+ private parseElseStatement;
23
+ private parseForStatement;
24
+ private parseSwitchStatement;
25
+ private parseCaseStatement;
26
+ private parseDefaultStatement;
27
+ private parseEmptyStatement;
28
+ private parseSimpleStatement;
29
+ private parseFallthroughStatement;
30
+ private parseBreakStatement;
31
+ private parseForContent;
32
+ private skipWhitespace;
33
+ private handleComment;
34
+ private getOriginalAttributeName;
35
+ }
36
+ //# sourceMappingURL=DomPreProcessor.d.ts.map