@fluffjs/cli 0.1.7 → 0.1.9
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 +39 -7
- package/CodeGenerator.js +1 -1
- package/ComponentCompiler.js +2 -1
- package/DevServer.d.ts +27 -0
- package/DevServer.js +109 -0
- package/interfaces/ServeOptions.d.ts +1 -0
- package/package.json +3 -1
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';
|
|
@@ -492,6 +493,18 @@ Examples:
|
|
|
492
493
|
const serveOptions = target.serve ?? {};
|
|
493
494
|
const port = serveOptions.port ?? 3000;
|
|
494
495
|
const host = serveOptions.host ?? 'localhost';
|
|
496
|
+
const esbuildPort = port + 1;
|
|
497
|
+
let proxyConfig = undefined;
|
|
498
|
+
if (serveOptions.proxyConfig) {
|
|
499
|
+
const proxyConfigPath = path.resolve(projectRoot, serveOptions.proxyConfig);
|
|
500
|
+
proxyConfig = DevServer.loadProxyConfig(proxyConfigPath);
|
|
501
|
+
if (proxyConfig) {
|
|
502
|
+
console.log(` ✓ Proxy config: ${serveOptions.proxyConfig}`);
|
|
503
|
+
for (const route of Object.keys(proxyConfig)) {
|
|
504
|
+
console.log(` ${route} -> ${proxyConfig[route].target}`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
495
508
|
const entryPoint = target.entryPoint
|
|
496
509
|
? path.join(srcDir, target.entryPoint)
|
|
497
510
|
: this.generateEntryPoint(srcDir, target.components);
|
|
@@ -567,13 +580,32 @@ Examples:
|
|
|
567
580
|
});
|
|
568
581
|
await ctx.watch();
|
|
569
582
|
console.log(' Watching for changes...');
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
583
|
+
if (proxyConfig) {
|
|
584
|
+
await ctx.serve({
|
|
585
|
+
servedir: outDir,
|
|
586
|
+
port: esbuildPort,
|
|
587
|
+
host: '127.0.0.1'
|
|
588
|
+
});
|
|
589
|
+
const devServer = new DevServer({
|
|
590
|
+
port,
|
|
591
|
+
host,
|
|
592
|
+
esbuildPort,
|
|
593
|
+
esbuildHost: '127.0.0.1',
|
|
594
|
+
proxyConfig
|
|
595
|
+
});
|
|
596
|
+
await devServer.start();
|
|
597
|
+
console.log(` Server running at http://${host}:${port}`);
|
|
598
|
+
console.log(' Press Ctrl+C to stop\n');
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
const { hosts, port: actualPort } = await ctx.serve({
|
|
602
|
+
servedir: outDir,
|
|
603
|
+
port,
|
|
604
|
+
host
|
|
605
|
+
});
|
|
606
|
+
console.log(` Server running at http://${hosts[0]}:${actualPort}`);
|
|
607
|
+
console.log(' Press Ctrl+C to stop\n');
|
|
608
|
+
}
|
|
577
609
|
}
|
|
578
610
|
generateEntryPoint(srcDir, componentPatterns) {
|
|
579
611
|
const componentFiles = this.findFiles(srcDir, componentPatterns);
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluffjs/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
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
|
},
|