@fluffjs/cli 0.1.8 → 0.1.10
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.d.ts +1 -0
- package/Cli.js +68 -16
- package/CodeGenerator.js +1 -1
- package/ComponentCompiler.js +2 -1
- package/DevServer.d.ts +27 -0
- package/DevServer.js +109 -0
- package/babel-plugin-reactive.js +2 -2
- package/interfaces/FluffTarget.d.ts +1 -1
- package/interfaces/ServeOptions.d.ts +1 -0
- package/package.json +3 -1
- package/types/FluffConfig.js +0 -1
package/Cli.d.ts
CHANGED
package/Cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import picomatch from 'picomatch';
|
|
|
7
7
|
import { gzipSync } from 'zlib';
|
|
8
8
|
import { generate } from './BabelHelpers.js';
|
|
9
9
|
import { ComponentCompiler } from './ComponentCompiler.js';
|
|
10
|
+
import { DevServer } from './DevServer.js';
|
|
10
11
|
import { fluffPlugin } from './fluff-esbuild-plugin.js';
|
|
11
12
|
import { Generator } from './Generator.js';
|
|
12
13
|
import { IndexHtmlTransformer } from './IndexHtmlTransformer.js';
|
|
@@ -195,7 +196,6 @@ Examples:
|
|
|
195
196
|
name: targetName,
|
|
196
197
|
srcDir: `src/${targetName}`,
|
|
197
198
|
outDir: `dist/${targetName}`,
|
|
198
|
-
components: ['**/*.component.ts'],
|
|
199
199
|
assets: ['**/*.html', '**/*.css']
|
|
200
200
|
};
|
|
201
201
|
console.log(`Added target '${targetName}' to fluff.json`);
|
|
@@ -213,7 +213,6 @@ Examples:
|
|
|
213
213
|
name: targetName,
|
|
214
214
|
srcDir: 'src',
|
|
215
215
|
outDir: 'dist',
|
|
216
|
-
components: ['**/*.component.ts'],
|
|
217
216
|
assets: ['**/*.html', '**/*.css']
|
|
218
217
|
}
|
|
219
218
|
};
|
|
@@ -305,7 +304,7 @@ Examples:
|
|
|
305
304
|
}
|
|
306
305
|
const entryPoint = target.entryPoint
|
|
307
306
|
? path.join(srcDir, target.entryPoint)
|
|
308
|
-
: this.generateEntryPoint(srcDir, target.
|
|
307
|
+
: this.generateEntryPoint(srcDir, target.exclude);
|
|
309
308
|
let inlineStyles = '';
|
|
310
309
|
if (target.styles && target.styles.length > 0) {
|
|
311
310
|
const styleContents = [];
|
|
@@ -492,9 +491,21 @@ Examples:
|
|
|
492
491
|
const serveOptions = target.serve ?? {};
|
|
493
492
|
const port = serveOptions.port ?? 3000;
|
|
494
493
|
const host = serveOptions.host ?? 'localhost';
|
|
494
|
+
const esbuildPort = port + 1;
|
|
495
|
+
let proxyConfig = undefined;
|
|
496
|
+
if (serveOptions.proxyConfig) {
|
|
497
|
+
const proxyConfigPath = path.resolve(projectRoot, serveOptions.proxyConfig);
|
|
498
|
+
proxyConfig = DevServer.loadProxyConfig(proxyConfigPath);
|
|
499
|
+
if (proxyConfig) {
|
|
500
|
+
console.log(` ✓ Proxy config: ${serveOptions.proxyConfig}`);
|
|
501
|
+
for (const route of Object.keys(proxyConfig)) {
|
|
502
|
+
console.log(` ${route} -> ${proxyConfig[route].target}`);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
495
506
|
const entryPoint = target.entryPoint
|
|
496
507
|
? path.join(srcDir, target.entryPoint)
|
|
497
|
-
: this.generateEntryPoint(srcDir, target.
|
|
508
|
+
: this.generateEntryPoint(srcDir, target.exclude);
|
|
498
509
|
let inlineStyles = '';
|
|
499
510
|
if (target.styles && target.styles.length > 0) {
|
|
500
511
|
const styleContents = [];
|
|
@@ -567,20 +578,39 @@ Examples:
|
|
|
567
578
|
});
|
|
568
579
|
await ctx.watch();
|
|
569
580
|
console.log(' Watching for changes...');
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
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
|
+
}
|
|
577
607
|
}
|
|
578
|
-
generateEntryPoint(srcDir,
|
|
579
|
-
const
|
|
580
|
-
for (const f of
|
|
581
|
-
console.log(` ✓
|
|
608
|
+
generateEntryPoint(srcDir, exclude = []) {
|
|
609
|
+
const tsFiles = this.findAllTsFiles(srcDir, exclude);
|
|
610
|
+
for (const f of tsFiles) {
|
|
611
|
+
console.log(` ✓ ${path.relative(srcDir, f)}`);
|
|
582
612
|
}
|
|
583
|
-
const importDecls =
|
|
613
|
+
const importDecls = tsFiles.map(f => {
|
|
584
614
|
const relativePath = './' + path.relative(srcDir, f)
|
|
585
615
|
.replace(/\\/g, '/');
|
|
586
616
|
return t.importDeclaration([], t.stringLiteral(relativePath));
|
|
@@ -591,6 +621,28 @@ Examples:
|
|
|
591
621
|
fs.writeFileSync(entryPath, entryContent);
|
|
592
622
|
return entryPath;
|
|
593
623
|
}
|
|
624
|
+
findAllTsFiles(dir, userExclude = []) {
|
|
625
|
+
const files = [];
|
|
626
|
+
const excludePatterns = ['*.spec.ts', '*.test.ts', '__generated_entry.ts', ...userExclude];
|
|
627
|
+
const walk = (currentDir) => {
|
|
628
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
629
|
+
for (const entry of entries) {
|
|
630
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
631
|
+
if (entry.isDirectory()) {
|
|
632
|
+
walk(fullPath);
|
|
633
|
+
}
|
|
634
|
+
else if (entry.isFile() && entry.name.endsWith('.ts')) {
|
|
635
|
+
const relativePath = path.relative(dir, fullPath).replace(/\\/g, '/');
|
|
636
|
+
const isExcluded = excludePatterns.some(pattern => this.matchGlob(entry.name, pattern) || this.matchGlob(relativePath, pattern));
|
|
637
|
+
if (!isExcluded) {
|
|
638
|
+
files.push(fullPath);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
walk(dir);
|
|
644
|
+
return files;
|
|
645
|
+
}
|
|
594
646
|
findFiles(dir, patterns) {
|
|
595
647
|
const files = [];
|
|
596
648
|
const walk = (currentDir) => {
|
package/CodeGenerator.js
CHANGED
|
@@ -52,7 +52,7 @@ export class CodeGenerator {
|
|
|
52
52
|
const fragment = parse5.parseFragment(html);
|
|
53
53
|
const styleElement = Parse5Helpers.createElement('style', []);
|
|
54
54
|
Parse5Helpers.appendText(styleElement, styles);
|
|
55
|
-
fragment.childNodes.
|
|
55
|
+
fragment.childNodes.push(styleElement);
|
|
56
56
|
styleElement.parentNode = fragment;
|
|
57
57
|
content = parse5.serialize(fragment);
|
|
58
58
|
}
|
package/ComponentCompiler.js
CHANGED
package/DevServer.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface ProxyConfigEntry {
|
|
2
|
+
target: string;
|
|
3
|
+
changeOrigin?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export type ProxyConfig = Record<string, ProxyConfigEntry>;
|
|
6
|
+
export interface DevServerOptions {
|
|
7
|
+
port: number;
|
|
8
|
+
host: string;
|
|
9
|
+
esbuildPort: number;
|
|
10
|
+
esbuildHost: string;
|
|
11
|
+
proxyConfig?: ProxyConfig;
|
|
12
|
+
}
|
|
13
|
+
export declare class DevServer {
|
|
14
|
+
private readonly options;
|
|
15
|
+
private readonly proxy;
|
|
16
|
+
private server;
|
|
17
|
+
constructor(options: DevServerOptions);
|
|
18
|
+
static loadProxyConfig(configPath: string): ProxyConfig | undefined;
|
|
19
|
+
private static isProxyEntry;
|
|
20
|
+
start(): Promise<number>;
|
|
21
|
+
stop(): void;
|
|
22
|
+
private handleRequest;
|
|
23
|
+
private findProxyEntry;
|
|
24
|
+
private proxyRequest;
|
|
25
|
+
private forwardToEsbuild;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=DevServer.d.ts.map
|
package/DevServer.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as http from 'node:http';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import httpProxy from 'http-proxy';
|
|
4
|
+
export class DevServer {
|
|
5
|
+
options;
|
|
6
|
+
proxy;
|
|
7
|
+
server = null;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.options = options;
|
|
10
|
+
this.proxy = httpProxy.createProxyServer({});
|
|
11
|
+
this.proxy.on('error', (err, req, res) => {
|
|
12
|
+
console.error('Proxy error:', err.message);
|
|
13
|
+
if (res instanceof http.ServerResponse && !res.headersSent) {
|
|
14
|
+
res.writeHead(502, { 'Content-Type': 'text/plain' });
|
|
15
|
+
res.end('Proxy error: ' + err.message);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
static loadProxyConfig(configPath) {
|
|
20
|
+
if (!fs.existsSync(configPath)) {
|
|
21
|
+
console.warn(`Proxy config not found: ${configPath}`);
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
26
|
+
const config = JSON.parse(content);
|
|
27
|
+
if (typeof config === 'object' && config !== null && !Array.isArray(config)) {
|
|
28
|
+
const result = {};
|
|
29
|
+
for (const [key, value] of Object.entries(config)) {
|
|
30
|
+
if (DevServer.isProxyEntry(value)) {
|
|
31
|
+
result[key] = {
|
|
32
|
+
target: value.target,
|
|
33
|
+
changeOrigin: typeof value.changeOrigin === 'boolean' ? value.changeOrigin : undefined
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.error('Failed to parse proxy config:', err);
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
static isProxyEntry(value) {
|
|
47
|
+
return typeof value === 'object' &&
|
|
48
|
+
value !== null &&
|
|
49
|
+
'target' in value &&
|
|
50
|
+
typeof value.target === 'string';
|
|
51
|
+
}
|
|
52
|
+
async start() {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
this.server = http.createServer((req, res) => {
|
|
55
|
+
this.handleRequest(req, res);
|
|
56
|
+
});
|
|
57
|
+
this.server.on('error', (err) => {
|
|
58
|
+
reject(err);
|
|
59
|
+
});
|
|
60
|
+
this.server.listen(this.options.port, () => {
|
|
61
|
+
const address = this.server?.address();
|
|
62
|
+
const port = typeof address === 'object' && address ? address.port : this.options.port;
|
|
63
|
+
resolve(port);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
stop() {
|
|
68
|
+
if (this.server) {
|
|
69
|
+
this.server.close();
|
|
70
|
+
this.server = null;
|
|
71
|
+
}
|
|
72
|
+
this.proxy.close();
|
|
73
|
+
}
|
|
74
|
+
handleRequest(req, res) {
|
|
75
|
+
const url = req.url ?? '/';
|
|
76
|
+
const [pathname] = url.split('?');
|
|
77
|
+
const proxyEntry = this.findProxyEntry(pathname);
|
|
78
|
+
if (proxyEntry) {
|
|
79
|
+
this.proxyRequest(req, res, proxyEntry);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this.forwardToEsbuild(req, res);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
findProxyEntry(pathname) {
|
|
86
|
+
if (!this.options.proxyConfig) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
for (const [pattern, entry] of Object.entries(this.options.proxyConfig)) {
|
|
90
|
+
if (pathname === pattern || pathname.startsWith(pattern + '/') || pathname.startsWith(pattern + '?')) {
|
|
91
|
+
return entry;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
proxyRequest(req, res, entry) {
|
|
97
|
+
const options = {
|
|
98
|
+
target: entry.target,
|
|
99
|
+
changeOrigin: entry.changeOrigin ?? false
|
|
100
|
+
};
|
|
101
|
+
this.proxy.web(req, res, options);
|
|
102
|
+
}
|
|
103
|
+
forwardToEsbuild(req, res) {
|
|
104
|
+
const options = {
|
|
105
|
+
target: `http://${this.options.esbuildHost}:${this.options.esbuildPort}`
|
|
106
|
+
};
|
|
107
|
+
this.proxy.web(req, res, options);
|
|
108
|
+
}
|
|
109
|
+
}
|
package/babel-plugin-reactive.js
CHANGED
|
@@ -287,7 +287,7 @@ export default function reactivePlugin() {
|
|
|
287
287
|
constructorStatements.push(t.expressionStatement(t.unaryExpression('void', t.memberExpression(t.thisExpression(), t.identifier(methodName)))));
|
|
288
288
|
for (const prop of mProps) {
|
|
289
289
|
if (reactiveProps.has(prop)) {
|
|
290
|
-
constructorStatements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(`__${prop}`)), t.identifier('onChange')), t.identifier('subscribe')), [t.arrowFunctionExpression([], t.callExpression(t.memberExpression(t.thisExpression(), t.identifier(methodName)), []))])));
|
|
290
|
+
constructorStatements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('__baseSubscriptions')), t.identifier('push')), [t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(`__${prop}`)), t.identifier('onChange')), t.identifier('subscribe')), [t.arrowFunctionExpression([], t.callExpression(t.memberExpression(t.thisExpression(), t.identifier(methodName)), [t.stringLiteral(prop)]))])])));
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
}
|
|
@@ -299,7 +299,7 @@ export default function reactivePlugin() {
|
|
|
299
299
|
for (const prop of wProps) {
|
|
300
300
|
if (reactiveProps.has(prop)) {
|
|
301
301
|
iife.push(t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('__subs'), t.identifier('push')), [
|
|
302
|
-
t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(`__${prop}`)), t.identifier('onChange')), t.identifier('subscribe')), [t.identifier('__cb')])
|
|
302
|
+
t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(`__${prop}`)), t.identifier('onChange')), t.identifier('subscribe')), [t.arrowFunctionExpression([], t.callExpression(t.identifier('__cb'), [t.stringLiteral(prop)]))])
|
|
303
303
|
])));
|
|
304
304
|
}
|
|
305
305
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluffjs/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"esbuild": "^0.27.2",
|
|
33
33
|
"he": "^1.2.0",
|
|
34
34
|
"html-minifier-terser": "^7.2.0",
|
|
35
|
+
"http-proxy": "^1.18.1",
|
|
35
36
|
"picomatch": "^4.0.2",
|
|
36
37
|
"parse5": "^8.0.0",
|
|
37
38
|
"parse5-sax-parser": "^8.0.0",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@types/he": "^1.2.3",
|
|
44
|
+
"@types/http-proxy": "^1.17.16",
|
|
43
45
|
"@types/jsesc": "^3.0.3",
|
|
44
46
|
"jsesc": "^3.1.0"
|
|
45
47
|
},
|