@easy-editor/easypack 0.0.2

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/dist/bin.js ADDED
@@ -0,0 +1,1076 @@
1
+ #!/usr/bin/env node
2
+ import mri from 'mri';
3
+ import pc from 'picocolors';
4
+ import { spawn } from 'node:child_process';
5
+ import { rollup } from 'rollup';
6
+ import { build } from 'esbuild';
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+ import { pathToFileURL } from 'node:url';
10
+ import babel from '@rollup/plugin-babel';
11
+ import commonjs from '@rollup/plugin-commonjs';
12
+ import nodeResolve from '@rollup/plugin-node-resolve';
13
+ import json from '@rollup/plugin-json';
14
+ import alias from '@rollup/plugin-alias';
15
+ import cleanup from 'rollup-plugin-cleanup';
16
+ import postcss from 'rollup-plugin-postcss';
17
+ import terser from '@rollup/plugin-terser';
18
+ import { createServer } from 'vite';
19
+ import react from '@vitejs/plugin-react';
20
+ import { WebSocketServer, WebSocket } from 'ws';
21
+ import * as prompts from '@clack/prompts';
22
+
23
+ const materialPreset = {
24
+ entry: {
25
+ main: 'src/index.tsx',
26
+ meta: 'src/meta.ts',
27
+ component: 'src/component.tsx'
28
+ },
29
+ output: {
30
+ dir: 'dist',
31
+ umd: true,
32
+ minify: true
33
+ },
34
+ css: {
35
+ scopedName: '[name]__[local]___[hash:base64:5]',
36
+ mode: 'inject'
37
+ },
38
+ jsxRuntime: 'automatic',
39
+ dev: {
40
+ port: 5001,
41
+ materialApi: true
42
+ }
43
+ };
44
+
45
+ const setterPreset = {
46
+ entry: {
47
+ main: 'src/index.ts'
48
+ },
49
+ output: {
50
+ dir: 'dist',
51
+ umd: true,
52
+ minify: true
53
+ },
54
+ css: {
55
+ scopedName: '[name]-[local]-[hash:base64:5]',
56
+ mode: 'extract',
57
+ extractFilename: 'index.css'
58
+ },
59
+ jsxRuntime: 'classic',
60
+ alias: {
61
+ '@': './src'
62
+ },
63
+ dev: {
64
+ port: 5002,
65
+ materialApi: false
66
+ }
67
+ };
68
+
69
+ const libraryPreset = {
70
+ entry: {
71
+ main: 'src/index.ts'
72
+ },
73
+ output: {
74
+ dir: 'dist',
75
+ umd: true,
76
+ minify: true
77
+ },
78
+ css: {
79
+ scopedName: '[name]__[local]___[hash:base64:5]',
80
+ mode: 'inject'
81
+ },
82
+ jsxRuntime: 'automatic',
83
+ dev: {
84
+ port: 5000,
85
+ materialApi: false
86
+ }
87
+ };
88
+
89
+ function getPreset(preset) {
90
+ switch (preset) {
91
+ case 'material':
92
+ return materialPreset;
93
+ case 'setter':
94
+ return setterPreset;
95
+ case 'library':
96
+ return libraryPreset;
97
+ default:
98
+ throw new Error(`Unknown preset: ${preset}`);
99
+ }
100
+ }
101
+
102
+ const DEFAULT_EXTERNALS$1 = ['react', 'react-dom', 'react/jsx-runtime'];
103
+ const DEFAULT_GLOBALS$1 = {
104
+ react: 'React',
105
+ 'react-dom': 'ReactDOM',
106
+ 'react/jsx-runtime': 'jsxRuntime'
107
+ };
108
+ const DEFAULT_ENTRY = {
109
+ main: 'src/index.ts',
110
+ meta: 'src/meta.ts',
111
+ component: 'src/component.tsx'
112
+ };
113
+ const DEFAULT_OUTPUT = {
114
+ dir: 'dist',
115
+ esm: false,
116
+ cjs: false,
117
+ umd: true,
118
+ minify: true,
119
+ sourcemap: false,
120
+ types: false
121
+ };
122
+ const DEFAULT_CSS = {
123
+ scopedName: '[name]__[local]___[hash:base64:5]',
124
+ mode: 'inject',
125
+ extractFilename: 'index.css'
126
+ };
127
+ const DEFAULT_DEV = {
128
+ port: 5001,
129
+ materialApi: true
130
+ };
131
+ function deriveGlobalName(pkgName) {
132
+ const withoutScope = pkgName.replace(/^@[^/]+\//, '');
133
+ if (withoutScope.startsWith('materials-dashboard-')) {
134
+ const componentName = withoutScope.replace('materials-dashboard-', '');
135
+ return `EasyEditorMaterials${toPascalCase(componentName)}`;
136
+ }
137
+ return `EasyEditor${toPascalCase(withoutScope)}`;
138
+ }
139
+ function toPascalCase(str) {
140
+ return str.split('-').map(part => part.charAt(0).toUpperCase() + part.slice(1)).join('');
141
+ }
142
+ function deepMerge(target, source) {
143
+ const result = {
144
+ ...target
145
+ };
146
+ for (const key of Object.keys(source)) {
147
+ const sourceValue = source[key];
148
+ const targetValue = target[key];
149
+ if (sourceValue !== undefined && typeof sourceValue === 'object' && sourceValue !== null && !Array.isArray(sourceValue) && typeof targetValue === 'object' && targetValue !== null && !Array.isArray(targetValue)) {
150
+ result[key] = deepMerge(targetValue, sourceValue);
151
+ } else if (sourceValue !== undefined) {
152
+ result[key] = sourceValue;
153
+ }
154
+ }
155
+ return result;
156
+ }
157
+ function resolveConfig(userConfig, pkg) {
158
+ const presetConfig = getPreset(userConfig.preset);
159
+ const merged = deepMerge(presetConfig, userConfig);
160
+ const resolved = {
161
+ preset: userConfig.preset,
162
+ globalName: merged.globalName || deriveGlobalName(pkg.name),
163
+ entry: {
164
+ main: merged.entry?.main || DEFAULT_ENTRY.main,
165
+ meta: merged.entry?.meta || DEFAULT_ENTRY.meta,
166
+ component: merged.entry?.component || DEFAULT_ENTRY.component
167
+ },
168
+ output: {
169
+ dir: merged.output?.dir || DEFAULT_OUTPUT.dir,
170
+ esm: merged.output?.esm ?? DEFAULT_OUTPUT.esm,
171
+ cjs: merged.output?.cjs ?? DEFAULT_OUTPUT.cjs,
172
+ umd: merged.output?.umd ?? DEFAULT_OUTPUT.umd,
173
+ minify: merged.output?.minify ?? DEFAULT_OUTPUT.minify,
174
+ sourcemap: merged.output?.sourcemap ?? DEFAULT_OUTPUT.sourcemap,
175
+ types: merged.output?.types ?? DEFAULT_OUTPUT.types
176
+ },
177
+ external: {
178
+ externals: merged.external?.externals || DEFAULT_EXTERNALS$1,
179
+ globals: {
180
+ ...DEFAULT_GLOBALS$1,
181
+ ...merged.external?.globals
182
+ }
183
+ },
184
+ css: {
185
+ scopedName: merged.css?.scopedName || DEFAULT_CSS.scopedName,
186
+ mode: merged.css?.mode || DEFAULT_CSS.mode,
187
+ extractFilename: merged.css?.extractFilename || DEFAULT_CSS.extractFilename
188
+ },
189
+ jsxRuntime: merged.jsxRuntime || 'automatic',
190
+ alias: merged.alias || {},
191
+ dev: {
192
+ port: merged.dev?.port || DEFAULT_DEV.port,
193
+ materialApi: merged.dev?.materialApi ?? DEFAULT_DEV.materialApi
194
+ },
195
+ vitePlugins: merged.vitePlugins || [],
196
+ pkg
197
+ };
198
+ return resolved;
199
+ }
200
+
201
+ const CONFIG_FILES = ['easypack.config.ts', 'easypack.config.js', 'easypack.config.mjs'];
202
+ function readPackageJson(cwd) {
203
+ const pkgPath = path.join(cwd, 'package.json');
204
+ if (!fs.existsSync(pkgPath)) {
205
+ throw new Error(`package.json not found in ${cwd}`);
206
+ }
207
+ const content = fs.readFileSync(pkgPath, 'utf-8');
208
+ const pkg = JSON.parse(content);
209
+ return {
210
+ name: pkg.name || 'unknown',
211
+ version: pkg.version || '0.0.0'
212
+ };
213
+ }
214
+ async function loadConfigFile(configPath) {
215
+ const ext = path.extname(configPath);
216
+ if (ext === '.js' || ext === '.mjs') {
217
+ const module = await import(pathToFileURL(configPath).href);
218
+ return module.default;
219
+ }
220
+ const result = await build({
221
+ entryPoints: [configPath],
222
+ bundle: true,
223
+ format: 'esm',
224
+ platform: 'node',
225
+ write: false,
226
+ external: ['@easy-editor/easypack']
227
+ });
228
+ const code = result.outputFiles[0].text;
229
+ const tempFile = path.join(path.dirname(configPath), '.easypack.config.tmp.mjs');
230
+ fs.writeFileSync(tempFile, code);
231
+ try {
232
+ const module = await import(pathToFileURL(tempFile).href);
233
+ return module.default;
234
+ } finally {
235
+ try {
236
+ fs.unlinkSync(tempFile);
237
+ } catch {
238
+ }
239
+ }
240
+ }
241
+ function findConfigFile(cwd, customPath) {
242
+ if (customPath) {
243
+ const fullPath = path.isAbsolute(customPath) ? customPath : path.join(cwd, customPath);
244
+ if (fs.existsSync(fullPath)) {
245
+ return fullPath;
246
+ }
247
+ throw new Error(`Config file not found: ${customPath}`);
248
+ }
249
+ for (const file of CONFIG_FILES) {
250
+ const fullPath = path.join(cwd, file);
251
+ if (fs.existsSync(fullPath)) {
252
+ return fullPath;
253
+ }
254
+ }
255
+ return null;
256
+ }
257
+ async function loadConfig(customConfigPath, cwd = process.cwd()) {
258
+ const configPath = findConfigFile(cwd, customConfigPath);
259
+ if (!configPath) {
260
+ throw new Error(`No config file found. Run \`easypack init\` to create one.\nSupported config files: ${CONFIG_FILES.join(', ')}`);
261
+ }
262
+ const pkg = readPackageJson(cwd);
263
+ const userConfig = await loadConfigFile(configPath);
264
+ return resolveConfig(userConfig, pkg);
265
+ }
266
+
267
+ const DEFAULT_EXTENSIONS = ['.js', '.ts', '.jsx', '.tsx'];
268
+
269
+ function createPlugins(options) {
270
+ const {
271
+ config,
272
+ minify = false,
273
+ cwd = process.cwd()
274
+ } = options;
275
+ const plugins = [];
276
+ if (Object.keys(config.alias).length > 0) {
277
+ const entries = Object.entries(config.alias).map(([find, replacement]) => ({
278
+ find,
279
+ replacement: path.resolve(cwd, replacement)
280
+ }));
281
+ plugins.push(alias({
282
+ entries
283
+ }));
284
+ }
285
+ plugins.push(nodeResolve({
286
+ extensions: DEFAULT_EXTENSIONS
287
+ }));
288
+ plugins.push(commonjs());
289
+ plugins.push(json());
290
+ plugins.push(postcss({
291
+ modules: {
292
+ generateScopedName: config.css.scopedName
293
+ },
294
+ autoModules: true,
295
+ minimize: true,
296
+ inject: config.css.mode === 'inject',
297
+ extract: config.css.mode === 'extract' ? config.css.extractFilename : false
298
+ }));
299
+ plugins.push(babel({
300
+ extensions: DEFAULT_EXTENSIONS,
301
+ exclude: 'node_modules/**',
302
+ babelrc: false,
303
+ babelHelpers: 'bundled',
304
+ presets: [['@babel/preset-react', {
305
+ runtime: config.jsxRuntime
306
+ }], ['@babel/preset-typescript', {
307
+ allowDeclareFields: true
308
+ }]]
309
+ }));
310
+ plugins.push(cleanup({
311
+ comments: ['some', /PURE/],
312
+ extensions: ['.js', '.ts']
313
+ }));
314
+ if (minify) {
315
+ plugins.push(terser());
316
+ }
317
+ return plugins;
318
+ }
319
+
320
+ function createBanner(pkgName, version, suffix) {
321
+ const suffixPart = suffix ? ` (${suffix})` : '';
322
+ return `/* ${pkgName} v${version}${suffixPart} */`;
323
+ }
324
+ function getExternals(config) {
325
+ return config.external.externals;
326
+ }
327
+ function getGlobals(config) {
328
+ return config.external.globals;
329
+ }
330
+ function getUmdName(globalName, type) {
331
+ switch (type) {
332
+ case 'meta':
333
+ return `${globalName}Meta`;
334
+ case 'component':
335
+ return `${globalName}Component`;
336
+ default:
337
+ return globalName;
338
+ }
339
+ }
340
+ function getFilePrefix(type) {
341
+ switch (type) {
342
+ case 'meta':
343
+ return 'meta';
344
+ case 'component':
345
+ return 'component';
346
+ default:
347
+ return 'index';
348
+ }
349
+ }
350
+ function createOutputConfigs(options) {
351
+ const {
352
+ config,
353
+ input,
354
+ type,
355
+ cwd = process.cwd()
356
+ } = options;
357
+ const configs = [];
358
+ const external = getExternals(config);
359
+ const globals = getGlobals(config);
360
+ const umdName = getUmdName(config.globalName, type);
361
+ const filePrefix = getFilePrefix(type);
362
+ const {
363
+ pkg,
364
+ output: outputConfig
365
+ } = config;
366
+ const {
367
+ minify
368
+ } = outputConfig;
369
+ const plugins = createPlugins({
370
+ config,
371
+ minify,
372
+ cwd
373
+ });
374
+ if (outputConfig.esm) {
375
+ const filename = minify ? `${filePrefix}.min.esm.js` : `${filePrefix}.esm.js`;
376
+ configs.push({
377
+ input,
378
+ output: {
379
+ file: `${outputConfig.dir}/${filename}`,
380
+ format: 'esm',
381
+ sourcemap: outputConfig.sourcemap,
382
+ banner: createBanner(pkg.name, pkg.version, minify ? 'minified' : undefined),
383
+ exports: 'named'
384
+ },
385
+ plugins,
386
+ external
387
+ });
388
+ }
389
+ if (outputConfig.umd) {
390
+ const filename = minify ? `${filePrefix}.min.js` : `${filePrefix}.js`;
391
+ configs.push({
392
+ input,
393
+ output: {
394
+ file: `${outputConfig.dir}/${filename}`,
395
+ format: 'umd',
396
+ name: umdName,
397
+ globals,
398
+ sourcemap: outputConfig.sourcemap,
399
+ banner: createBanner(pkg.name, pkg.version, minify ? 'minified' : undefined),
400
+ exports: 'named'
401
+ },
402
+ plugins,
403
+ external
404
+ });
405
+ }
406
+ if (type === 'full' && outputConfig.cjs) {
407
+ const filename = minify ? `${filePrefix}.min.cjs` : `${filePrefix}.cjs`;
408
+ configs.push({
409
+ input,
410
+ output: {
411
+ file: `${outputConfig.dir}/${filename}`,
412
+ format: 'cjs',
413
+ sourcemap: outputConfig.sourcemap,
414
+ exports: 'named'
415
+ },
416
+ plugins,
417
+ external
418
+ });
419
+ }
420
+ return configs;
421
+ }
422
+
423
+ function entryExists(entry, cwd) {
424
+ const fullPath = path.join(cwd, entry);
425
+ return fs.existsSync(fullPath);
426
+ }
427
+ function createRollupConfig(options) {
428
+ const {
429
+ config,
430
+ cwd = process.cwd()
431
+ } = options;
432
+ const configs = [];
433
+ if (config.preset === 'material') {
434
+ if (config.entry.meta && entryExists(config.entry.meta, cwd)) {
435
+ configs.push(...createOutputConfigs({
436
+ config,
437
+ input: config.entry.meta,
438
+ type: 'meta',
439
+ cwd
440
+ }));
441
+ }
442
+ if (config.entry.component && entryExists(config.entry.component, cwd)) {
443
+ configs.push(...createOutputConfigs({
444
+ config,
445
+ input: config.entry.component,
446
+ type: 'component',
447
+ cwd
448
+ }));
449
+ }
450
+ }
451
+ if (config.entry.main && entryExists(config.entry.main, cwd)) {
452
+ configs.push(...createOutputConfigs({
453
+ config,
454
+ input: config.entry.main,
455
+ type: 'full',
456
+ cwd
457
+ }));
458
+ } else {
459
+ throw new Error(`Main entry file not found: ${config.entry.main}`);
460
+ }
461
+ return configs;
462
+ }
463
+
464
+ async function generateTypes(config) {
465
+ return new Promise((resolve, reject) => {
466
+ const args = ['tsc', '--declaration', '--emitDeclarationOnly', '--outDir', config.output.dir];
467
+ const tsc = spawn('npx', args, {
468
+ stdio: 'pipe'
469
+ });
470
+ let stderr = '';
471
+ tsc.stderr?.on('data', data => {
472
+ stderr += data.toString();
473
+ });
474
+ tsc.on('close', code => {
475
+ if (code === 0) {
476
+ resolve([`${config.output.dir}/index.d.ts`]);
477
+ } else {
478
+ reject(new Error(`TypeScript compilation failed:\n${stderr}`));
479
+ }
480
+ });
481
+ tsc.on('error', reject);
482
+ });
483
+ }
484
+ async function buildCommand(argv) {
485
+ const startTime = Date.now();
486
+ console.log(pc.cyan('Building for production...\n'));
487
+ const config = await loadConfig(argv.config);
488
+ console.log(pc.gray(` Package: ${config.pkg.name}@${config.pkg.version}`));
489
+ console.log(pc.gray(` Preset: ${config.preset}`));
490
+ console.log(pc.gray(` Output: ${config.output.dir}/\n`));
491
+ const rollupConfigs = createRollupConfig({
492
+ config
493
+ });
494
+ let fileCount = 0;
495
+ for (const rollupConfig of rollupConfigs) {
496
+ const bundle = await rollup(rollupConfig);
497
+ const outputs = Array.isArray(rollupConfig.output) ? rollupConfig.output : [rollupConfig.output];
498
+ for (const output of outputs) {
499
+ if (output && output.file) {
500
+ await bundle.write(output);
501
+ console.log(pc.green(` ✓ ${output.file}`));
502
+ fileCount++;
503
+ }
504
+ }
505
+ await bundle.close();
506
+ }
507
+ if (config.output.types) {
508
+ try {
509
+ const typeFiles = await generateTypes(config);
510
+ for (const file of typeFiles) {
511
+ console.log(pc.green(` ✓ ${file}`));
512
+ fileCount++;
513
+ }
514
+ } catch (error) {
515
+ console.log(pc.yellow(` ⚠ Types generation skipped (no tsconfig.json or tsc error)`));
516
+ }
517
+ }
518
+ const duration = Date.now() - startTime;
519
+ console.log('');
520
+ console.log(pc.green(`Build completed! `) + pc.gray(`(${fileCount} files in ${duration}ms)`));
521
+ }
522
+
523
+ const DEFAULT_EXTERNALS = ['react', 'react-dom', 'react/jsx-runtime'];
524
+ const DEFAULT_GLOBALS = {
525
+ react: 'React',
526
+ 'react-dom': 'ReactDOM',
527
+ 'react/jsx-runtime': 'jsxRuntime'
528
+ };
529
+ const VIRTUAL_PREFIX = '\0virtual:external:';
530
+ function generateReactCode(globalName) {
531
+ return `
532
+ // External module: react -> window.${globalName}
533
+ const React = window.${globalName};
534
+ if (!React) {
535
+ throw new Error(
536
+ 'External dependency "react" (window.${globalName}) is not available. ' +
537
+ 'Make sure the parent application has loaded it globally.'
538
+ );
539
+ }
540
+
541
+ export default React;
542
+
543
+ export const {
544
+ useState, useEffect, useContext, useReducer, useCallback, useMemo,
545
+ useRef, useImperativeHandle, useLayoutEffect, useDebugValue,
546
+ useDeferredValue, useTransition, useId, useSyncExternalStore,
547
+ Fragment, StrictMode, Suspense, createElement, createContext,
548
+ forwardRef, lazy, memo, startTransition, Component, PureComponent,
549
+ Children, cloneElement, isValidElement,
550
+ } = React;
551
+ `;
552
+ }
553
+ function generateReactDomCode(globalName) {
554
+ return `
555
+ // External module: react-dom -> window.${globalName}
556
+ const ReactDOM = window.${globalName};
557
+ if (!ReactDOM) {
558
+ throw new Error(
559
+ 'External dependency "react-dom" (window.${globalName}) is not available. ' +
560
+ 'Make sure the parent application has loaded it globally.'
561
+ );
562
+ }
563
+
564
+ export default ReactDOM;
565
+
566
+ export const {
567
+ createRoot, hydrateRoot, render, hydrate,
568
+ unmountComponentAtNode, findDOMNode, flushSync,
569
+ } = ReactDOM;
570
+ `;
571
+ }
572
+ function generateJsxRuntimeCode(globalName) {
573
+ return `
574
+ // External module: react/jsx-runtime -> window.${globalName}
575
+ const jsxRuntime = window.${globalName};
576
+ if (!jsxRuntime) {
577
+ throw new Error(
578
+ 'External dependency "react/jsx-runtime" (window.${globalName}) is not available. ' +
579
+ 'Make sure the parent application has loaded it globally.'
580
+ );
581
+ }
582
+
583
+ export const { jsx, jsxs, Fragment } = jsxRuntime;
584
+ export default jsxRuntime;
585
+ `;
586
+ }
587
+ function generateEasyEditorCoreCode(globalName) {
588
+ return `
589
+ // External module: @easy-editor/core -> window.${globalName}
590
+ const EasyEditorCore = window.${globalName};
591
+ if (!EasyEditorCore) {
592
+ throw new Error(
593
+ 'External dependency "@easy-editor/core" (window.${globalName}) is not available. ' +
594
+ 'Make sure the parent application has loaded it globally.'
595
+ );
596
+ }
597
+
598
+ export default EasyEditorCore;
599
+
600
+ // 动态导出所有属性
601
+ const keys = Object.keys(EasyEditorCore || {});
602
+ keys.forEach(key => {
603
+ Object.defineProperty(module.exports, key, {
604
+ get: () => EasyEditorCore[key],
605
+ enumerable: true,
606
+ });
607
+ });
608
+ `;
609
+ }
610
+ function generateGenericCode(moduleName, globalName) {
611
+ return `
612
+ // External module: ${moduleName} -> window.${globalName}
613
+ const mod = window.${globalName};
614
+ if (!mod) {
615
+ throw new Error(
616
+ 'External dependency "${moduleName}" (window.${globalName}) is not available. ' +
617
+ 'Make sure the parent application has loaded it globally.'
618
+ );
619
+ }
620
+ export default mod;
621
+ `;
622
+ }
623
+ function externalDepsPlugin(options = {}) {
624
+ const externals = options.externals || DEFAULT_EXTERNALS;
625
+ const globals = {
626
+ ...DEFAULT_GLOBALS,
627
+ ...options.globals
628
+ };
629
+ return {
630
+ name: 'vite-plugin-external-deps',
631
+ enforce: 'pre',
632
+ resolveId(id) {
633
+ if (externals.includes(id)) {
634
+ return VIRTUAL_PREFIX + id;
635
+ }
636
+ return null;
637
+ },
638
+ load(id) {
639
+ if (!id.startsWith(VIRTUAL_PREFIX)) {
640
+ return null;
641
+ }
642
+ const moduleName = id.slice(VIRTUAL_PREFIX.length);
643
+ const globalName = globals[moduleName];
644
+ if (!globalName) {
645
+ throw new Error(`[vite-plugin-external-deps] No global mapping found for "${moduleName}". ` + 'Please add it to the globals option.');
646
+ }
647
+ switch (moduleName) {
648
+ case 'react':
649
+ return generateReactCode(globalName);
650
+ case 'react-dom':
651
+ return generateReactDomCode(globalName);
652
+ case 'react/jsx-runtime':
653
+ return generateJsxRuntimeCode(globalName);
654
+ case '@easy-editor/core':
655
+ return generateEasyEditorCoreCode(globalName);
656
+ default:
657
+ return generateGenericCode(moduleName, globalName);
658
+ }
659
+ },
660
+ config() {
661
+ return {
662
+ build: {
663
+ rollupOptions: {
664
+ external: externals,
665
+ output: {
666
+ globals
667
+ }
668
+ }
669
+ }
670
+ };
671
+ }
672
+ };
673
+ }
674
+
675
+ function materialDevPlugin(options = {}) {
676
+ const {
677
+ entry = '/src/index.tsx'
678
+ } = options;
679
+ let server;
680
+ let wss = null;
681
+ const clients = new Set();
682
+ function broadcast(message) {
683
+ const data = JSON.stringify(message);
684
+ for (const client of clients) {
685
+ if (client.readyState === WebSocket.OPEN) {
686
+ client.send(data);
687
+ }
688
+ }
689
+ }
690
+ return {
691
+ name: 'vite-plugin-material-dev',
692
+ configureServer(_server) {
693
+ server = _server;
694
+ wss = new WebSocketServer({
695
+ noServer: true
696
+ });
697
+ server.httpServer?.on('upgrade', (request, socket, head) => {
698
+ if (request.url === '/ws' || request.url === '/__material_ws__') {
699
+ wss?.handleUpgrade(request, socket, head, ws => {
700
+ wss?.emit('connection', ws, request);
701
+ });
702
+ }
703
+ });
704
+ wss.on('connection', ws => {
705
+ clients.add(ws);
706
+ ws.send(JSON.stringify({
707
+ type: 'connected',
708
+ message: 'Material dev server connected',
709
+ timestamp: Date.now()
710
+ }));
711
+ ws.on('close', () => {
712
+ clients.delete(ws);
713
+ });
714
+ ws.on('error', () => {
715
+ clients.delete(ws);
716
+ });
717
+ });
718
+ server.middlewares.use((req, res, next) => {
719
+ if (req.method === 'OPTIONS') {
720
+ res.setHeader('Access-Control-Allow-Origin', '*');
721
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
722
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
723
+ res.statusCode = 204;
724
+ res.end();
725
+ return;
726
+ }
727
+ next();
728
+ });
729
+ server.middlewares.use('/api/health', (_req, res) => {
730
+ res.setHeader('Content-Type', 'application/json');
731
+ res.setHeader('Access-Control-Allow-Origin', '*');
732
+ res.end(JSON.stringify({
733
+ status: 'ok',
734
+ timestamp: Date.now(),
735
+ server: 'vite',
736
+ wsPath: '/ws'
737
+ }));
738
+ });
739
+ server.middlewares.use('/api/material', async (_req, res) => {
740
+ res.setHeader('Content-Type', 'application/json');
741
+ res.setHeader('Access-Control-Allow-Origin', '*');
742
+ try {
743
+ const module = await server.ssrLoadModule(entry);
744
+ const meta = module.meta || module.default?.meta;
745
+ const component = module.component || module.default;
746
+ if (!meta) {
747
+ res.statusCode = 400;
748
+ res.end(JSON.stringify({
749
+ error: 'Material meta not found. Make sure to export "meta" from the entry file.'
750
+ }));
751
+ return;
752
+ }
753
+ const materialInfo = {
754
+ name: meta.componentName,
755
+ title: meta.title,
756
+ version: meta.npm?.version || '0.0.0-dev',
757
+ group: meta.group,
758
+ category: meta.category,
759
+ entry,
760
+ hasComponent: !!component,
761
+ hasMeta: !!meta,
762
+ hasConfigure: !!meta.configure,
763
+ hasSnippets: Array.isArray(meta.snippets) && meta.snippets.length > 0,
764
+ wsPath: '/ws'
765
+ };
766
+ res.end(JSON.stringify(materialInfo));
767
+ } catch (error) {
768
+ res.statusCode = 500;
769
+ res.end(JSON.stringify({
770
+ error: error instanceof Error ? error.message : String(error)
771
+ }));
772
+ }
773
+ });
774
+ server.httpServer?.once('listening', () => {
775
+ const address = server.httpServer?.address();
776
+ const port = typeof address === 'object' && address ? address.port : 5001;
777
+ const host = server.config.server.host || 'localhost';
778
+ setTimeout(() => {
779
+ console.log('');
780
+ console.log('\x1b[36m%s\x1b[0m', ' Material Dev Server Ready');
781
+ console.log('');
782
+ console.log(` Health Check: http://${host}:${port}/api/health`);
783
+ console.log(` Material Info: http://${host}:${port}/api/material`);
784
+ console.log(` Module Entry: http://${host}:${port}${entry}`);
785
+ console.log(` WebSocket: ws://${host}:${port}/ws`);
786
+ console.log('');
787
+ console.log('\x1b[33m%s\x1b[0m', ' Connect this URL in EasyEditor to start debugging');
788
+ console.log('');
789
+ }, 100);
790
+ });
791
+ },
792
+ handleHotUpdate({
793
+ file,
794
+ modules
795
+ }) {
796
+ broadcast({
797
+ type: 'update',
798
+ file,
799
+ timestamp: Date.now(),
800
+ modules: modules.map(m => m.id)
801
+ });
802
+ return;
803
+ },
804
+ closeBundle() {
805
+ if (wss) {
806
+ for (const client of clients) {
807
+ client.close();
808
+ }
809
+ clients.clear();
810
+ wss.close();
811
+ wss = null;
812
+ }
813
+ }
814
+ };
815
+ }
816
+
817
+ function createViteConfig(options) {
818
+ const {
819
+ config,
820
+ cwd = process.cwd()
821
+ } = options;
822
+ const {
823
+ externals,
824
+ globals
825
+ } = config.external;
826
+ const plugins = [react()];
827
+ plugins.push(externalDepsPlugin({
828
+ externals,
829
+ globals
830
+ }));
831
+ if (config.dev.materialApi) {
832
+ plugins.push(materialDevPlugin({
833
+ entry: `/${config.entry.main}`
834
+ }));
835
+ }
836
+ if (config.vitePlugins.length > 0) {
837
+ plugins.push(...config.vitePlugins);
838
+ }
839
+ const aliasConfig = {};
840
+ for (const [key, value] of Object.entries(config.alias)) {
841
+ aliasConfig[key] = path.resolve(cwd, value);
842
+ }
843
+ return {
844
+ plugins,
845
+ server: {
846
+ port: config.dev.port,
847
+ host: 'localhost',
848
+ cors: true,
849
+ hmr: {
850
+ port: config.dev.port
851
+ }
852
+ },
853
+ build: {
854
+ target: 'esnext',
855
+ rollupOptions: {
856
+ external: externals,
857
+ output: {
858
+ globals
859
+ }
860
+ }
861
+ },
862
+ resolve: {
863
+ alias: aliasConfig,
864
+ dedupe: ['react', 'react-dom']
865
+ },
866
+ css: {
867
+ modules: {
868
+ generateScopedName: config.css.scopedName
869
+ }
870
+ }
871
+ };
872
+ }
873
+
874
+ async function devCommand(argv) {
875
+ console.log(pc.cyan('Starting development server...\n'));
876
+ const config = await loadConfig(argv.config);
877
+ console.log(pc.gray(` Package: ${config.pkg.name}@${config.pkg.version}`));
878
+ console.log(pc.gray(` Preset: ${config.preset}`));
879
+ console.log(pc.gray(` Port: ${config.dev.port}\n`));
880
+ const viteConfig = createViteConfig({
881
+ config
882
+ });
883
+ const server = await createServer(viteConfig);
884
+ await server.listen();
885
+ server.printUrls();
886
+ const shutdown = async () => {
887
+ console.log(pc.yellow('\nShutting down...'));
888
+ await server.close();
889
+ process.exit(0);
890
+ };
891
+ process.on('SIGINT', shutdown);
892
+ process.on('SIGTERM', shutdown);
893
+ }
894
+
895
+ const MATERIAL_TEMPLATE = `/**
896
+ * @easy-editor/easypack configuration
897
+ * @type {import('@easy-editor/easypack').EasypackConfig}
898
+ */
899
+ export default {
900
+ preset: 'material',
901
+ // globalName 自动从 package.json 推导
902
+ dev: {
903
+ port: 5001,
904
+ },
905
+ // output: {
906
+ // umd: true, // UMD 格式(默认开启)
907
+ // esm: false, // ESM 格式
908
+ // cjs: false, // CJS 格式
909
+ // minify: true, // 压缩输出(默认开启)
910
+ // types: false, // TypeScript 类型声明
911
+ // },
912
+ }
913
+ `;
914
+ const SETTER_TEMPLATE = `/**
915
+ * @easy-editor/easypack configuration
916
+ * @type {import('@easy-editor/easypack').EasypackConfig}
917
+ */
918
+ export default {
919
+ preset: 'setter',
920
+ globalName: 'EasyEditorSetters',
921
+ // output: {
922
+ // umd: true, // UMD 格式(默认开启)
923
+ // esm: false, // ESM 格式
924
+ // cjs: false, // CJS 格式
925
+ // minify: true, // 压缩输出(默认开启)
926
+ // types: false, // TypeScript 类型声明
927
+ // },
928
+ }
929
+ `;
930
+ const LIBRARY_TEMPLATE = `/**
931
+ * @easy-editor/easypack configuration
932
+ * @type {import('@easy-editor/easypack').EasypackConfig}
933
+ */
934
+ export default {
935
+ preset: 'library',
936
+ // globalName: 'MyLibrary',
937
+ // output: {
938
+ // umd: true, // UMD 格式(默认开启)
939
+ // esm: false, // ESM 格式
940
+ // cjs: false, // CJS 格式
941
+ // minify: true, // 压缩输出(默认开启)
942
+ // types: false, // TypeScript 类型声明
943
+ // },
944
+ }
945
+ `;
946
+ async function initCommand(argv) {
947
+ const cwd = process.cwd();
948
+ const configPath = path.join(cwd, 'easypack.config.ts');
949
+ if (fs.existsSync(configPath)) {
950
+ const overwrite = await prompts.confirm({
951
+ message: 'easypack.config.ts already exists. Overwrite?'
952
+ });
953
+ if (prompts.isCancel(overwrite) || !overwrite) {
954
+ prompts.cancel('Operation cancelled');
955
+ return;
956
+ }
957
+ }
958
+ let preset = argv.preset;
959
+ if (!preset) {
960
+ const selected = await prompts.select({
961
+ message: 'Select a preset:',
962
+ options: [{
963
+ label: 'material',
964
+ value: 'material',
965
+ hint: 'For EasyMaterials components'
966
+ }, {
967
+ label: 'setter',
968
+ value: 'setter',
969
+ hint: 'For EasySetters package'
970
+ }, {
971
+ label: 'library',
972
+ value: 'library',
973
+ hint: 'For general React libraries'
974
+ }]
975
+ });
976
+ if (prompts.isCancel(selected)) {
977
+ prompts.cancel('Operation cancelled');
978
+ return;
979
+ }
980
+ preset = selected;
981
+ }
982
+ let template;
983
+ switch (preset) {
984
+ case 'material':
985
+ template = MATERIAL_TEMPLATE;
986
+ break;
987
+ case 'setter':
988
+ template = SETTER_TEMPLATE;
989
+ break;
990
+ case 'library':
991
+ default:
992
+ template = LIBRARY_TEMPLATE;
993
+ break;
994
+ }
995
+ fs.writeFileSync(configPath, template);
996
+ console.log('');
997
+ console.log(pc.green('✓ Created easypack.config.ts'));
998
+ console.log('');
999
+ console.log(pc.gray('Next steps:'));
1000
+ console.log(pc.gray(' 1. Review and customize the config file'));
1001
+ console.log(pc.gray(' 2. Run `easypack build` to build for production'));
1002
+ console.log(pc.gray(' 3. Run `easypack dev` to start dev server'));
1003
+ console.log('');
1004
+ }
1005
+
1006
+ const VERSION = '0.0.1';
1007
+ const helpMessage = `
1008
+ ${pc.bold(pc.cyan('@easy-editor/easypack'))} - Build and dev tools for EasyEditor ecosystem
1009
+
1010
+ ${pc.bold('Usage:')}
1011
+ easypack <command> [options]
1012
+
1013
+ ${pc.bold('Commands:')}
1014
+ build Build for production
1015
+ dev Start development server
1016
+ init Generate config file template
1017
+
1018
+ ${pc.bold('Options:')}
1019
+ -c, --config <file> Config file path
1020
+ -h, --help Show help
1021
+ -v, --version Show version
1022
+
1023
+ ${pc.bold('Examples:')}
1024
+ ${pc.gray('# Build for production')}
1025
+ easypack build
1026
+
1027
+ ${pc.gray('# Start dev server')}
1028
+ easypack dev
1029
+
1030
+ ${pc.gray('# Generate config template')}
1031
+ easypack init
1032
+ `;
1033
+ async function cli() {
1034
+ const argv = mri(process.argv.slice(2), {
1035
+ alias: {
1036
+ h: 'help',
1037
+ v: 'version',
1038
+ c: 'config',
1039
+ p: 'preset'
1040
+ },
1041
+ boolean: ['help', 'version'],
1042
+ string: ['config', 'preset']
1043
+ });
1044
+ if (argv.version) {
1045
+ console.log(VERSION);
1046
+ return;
1047
+ }
1048
+ if (argv.help || argv._.length === 0) {
1049
+ console.log(helpMessage);
1050
+ return;
1051
+ }
1052
+ const command = argv._[0];
1053
+ try {
1054
+ switch (command) {
1055
+ case 'build':
1056
+ await buildCommand(argv);
1057
+ break;
1058
+ case 'dev':
1059
+ await devCommand(argv);
1060
+ break;
1061
+ case 'init':
1062
+ await initCommand(argv);
1063
+ break;
1064
+ default:
1065
+ console.log(pc.red(`Unknown command: ${command}`));
1066
+ console.log(helpMessage);
1067
+ process.exit(1);
1068
+ }
1069
+ } catch (error) {
1070
+ console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
1071
+ process.exit(1);
1072
+ }
1073
+ }
1074
+
1075
+ cli();
1076
+ //# sourceMappingURL=bin.js.map