nativescript-web-adapter 0.1.5 → 0.1.6
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/README.md +32 -14
- package/angular/components/absolute-layout.component.ts +18 -0
- package/angular/components/action-bar.component.ts +16 -0
- package/angular/components/action-item.component.ts +22 -0
- package/angular/components/activity-indicator.component.ts +32 -0
- package/angular/components/button.component.ts +45 -0
- package/angular/components/date-picker.component.ts +35 -0
- package/angular/components/directives/absolute-position.directive.ts +31 -0
- package/angular/components/directives/grid-position.directive.ts +43 -0
- package/angular/components/dock-layout.component.ts +46 -0
- package/angular/components/flexbox-layout.component.ts +16 -0
- package/angular/components/frame.component.ts +9 -0
- package/angular/components/grid-layout.component.ts +48 -0
- package/angular/components/html-view.component.ts +23 -0
- package/angular/components/image-cache-it.component.ts +21 -0
- package/angular/components/image.component.ts +22 -0
- package/angular/components/index.ts +156 -0
- package/angular/components/label.component.ts +27 -0
- package/angular/components/list-picker.component.ts +33 -0
- package/angular/components/list-view.component.ts +22 -0
- package/angular/components/navigation-button.component.ts +36 -0
- package/angular/components/page.component.ts +21 -0
- package/angular/components/placeholder.component.ts +16 -0
- package/angular/components/progress.component.ts +20 -0
- package/angular/components/root-layout.component.ts +19 -0
- package/angular/components/scroll-view.component.ts +17 -0
- package/angular/components/search-bar.component.ts +34 -0
- package/angular/components/segmented-bar-item.component.ts +27 -0
- package/angular/components/segmented-bar.component.ts +101 -0
- package/angular/components/slider.component.ts +41 -0
- package/angular/components/stack-layout.component.ts +17 -0
- package/angular/components/switch.component.ts +62 -0
- package/angular/components/tab-view-item.component.ts +27 -0
- package/angular/components/tab-view.component.ts +89 -0
- package/angular/components/text-field.component.ts +38 -0
- package/angular/components/text-view.component.ts +29 -0
- package/angular/components/time-picker.component.ts +27 -0
- package/angular/components/web-view.component.ts +25 -0
- package/angular/components/wrap-layout.component.ts +17 -0
- package/angular/composables/dialogs.ts +54 -0
- package/angular/composables/index.ts +6 -0
- package/angular/composables/ref.ts +8 -0
- package/angular/composables/useActionBar.ts +20 -0
- package/angular/composables/useFrame.ts +26 -0
- package/angular/composables/usePage.ts +26 -0
- package/angular/index.ts +8 -0
- package/core/components/Button.vue +2 -2
- package/core/components/GridLayout.vue +2 -2
- package/core/components/HtmlView.vue +2 -2
- package/core/components/Image.vue +2 -2
- package/core/components/ImageCacheIt.vue +2 -2
- package/core/components/Label.vue +2 -2
- package/core/components/ListPicker.vue +2 -2
- package/core/components/NavigationButton.vue +2 -2
- package/core/components/Progress.vue +2 -2
- package/core/components/SearchBar.vue +2 -2
- package/core/components/Slider.vue +2 -2
- package/core/components/Switch.vue +2 -2
- package/core/components/TextField.vue +2 -2
- package/core/components/TextView.vue +2 -2
- package/core/components/WebView.vue +2 -2
- package/core/composables/dialogs.ts +5 -5
- package/dist/nativescript-web-adapter.es.js +22375 -45
- package/dist/nativescript-web-adapter.umd.js +534 -1
- package/package.json +37 -7
- package/tools/cli.cjs +17 -4
- package/tools/create-web-platform.cjs +40 -18
- package/tools/modules/appPatch.cjs +125 -1
- package/tools/modules/templates.cjs +161 -84
- package/tools/modules/transform.cjs +69 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nativescript-web-adapter",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Web adapter for NativeScript applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/nativescript-web-adapter.umd.js",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"build": "vite build",
|
|
20
20
|
"preview": "vite preview",
|
|
21
21
|
"type-check": "vue-tsc --noEmit",
|
|
22
|
+
"test": "vitest run",
|
|
22
23
|
"prepublishOnly": "npm run build"
|
|
23
24
|
},
|
|
24
25
|
"bin": {
|
|
@@ -28,20 +29,49 @@
|
|
|
28
29
|
"dist",
|
|
29
30
|
"tools",
|
|
30
31
|
"templates",
|
|
31
|
-
"core"
|
|
32
|
+
"core",
|
|
33
|
+
"angular"
|
|
32
34
|
],
|
|
33
35
|
"dependencies": {
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
+
"commander": "^11.0.0",
|
|
37
|
+
"vue": "^3.0.0"
|
|
36
38
|
},
|
|
37
39
|
"devDependencies": {
|
|
40
|
+
"@angular/common": "^18.2.0",
|
|
41
|
+
"@angular/core": "^18.2.0",
|
|
42
|
+
"@angular/platform-browser": "^18.2.0",
|
|
43
|
+
"@angular/router": "^18.2.0",
|
|
38
44
|
"@types/node": "^20.0.0",
|
|
39
|
-
"typescript": "^5.0.0",
|
|
40
45
|
"@vitejs/plugin-vue": "^5.0.0",
|
|
46
|
+
"rxjs": "^7.8.1",
|
|
47
|
+
"typescript": "^5.0.0",
|
|
41
48
|
"vite": "^5.0.0",
|
|
42
|
-
"
|
|
49
|
+
"vitest": "^2.1.8",
|
|
50
|
+
"vue-tsc": "^2.2.12"
|
|
43
51
|
},
|
|
44
52
|
"peerDependencies": {
|
|
45
|
-
"vue": "^3.0.0"
|
|
53
|
+
"vue": "^3.0.0",
|
|
54
|
+
"@angular/core": "^18.0.0",
|
|
55
|
+
"@angular/common": "^18.0.0",
|
|
56
|
+
"@angular/platform-browser": "^18.0.0",
|
|
57
|
+
"@angular/router": "^18.0.0",
|
|
58
|
+
"rxjs": "^7.0.0"
|
|
59
|
+
},
|
|
60
|
+
"peerDependenciesMeta": {
|
|
61
|
+
"@angular/core": {
|
|
62
|
+
"optional": true
|
|
63
|
+
},
|
|
64
|
+
"@angular/common": {
|
|
65
|
+
"optional": true
|
|
66
|
+
},
|
|
67
|
+
"@angular/platform-browser": {
|
|
68
|
+
"optional": true
|
|
69
|
+
},
|
|
70
|
+
"@angular/router": {
|
|
71
|
+
"optional": true
|
|
72
|
+
},
|
|
73
|
+
"rxjs": {
|
|
74
|
+
"optional": true
|
|
75
|
+
}
|
|
46
76
|
}
|
|
47
77
|
}
|
package/tools/cli.cjs
CHANGED
|
@@ -18,11 +18,11 @@ program
|
|
|
18
18
|
|
|
19
19
|
program
|
|
20
20
|
.command('build')
|
|
21
|
-
.description('
|
|
21
|
+
.description('根据项目配置自动选择框架并在对应 platforms/<framework> 内构建')
|
|
22
22
|
.option('--skip-install', '跳过依赖安装')
|
|
23
23
|
.action(async (opts) => {
|
|
24
|
-
await createWebPlatform();
|
|
25
|
-
const webDir = path.join(process.cwd(), 'platforms', 'vue');
|
|
24
|
+
const res = await createWebPlatform();
|
|
25
|
+
const webDir = res?.webDir || path.join(process.cwd(), 'platforms', 'vue');
|
|
26
26
|
if (!opts.skipInstall) {
|
|
27
27
|
execSync('npm install', { stdio: 'inherit', cwd: webDir });
|
|
28
28
|
}
|
|
@@ -34,7 +34,7 @@ program
|
|
|
34
34
|
.description('生成 Vue 平台并在 platforms/vue 内启动开发服务器')
|
|
35
35
|
.option('--skip-install', '跳过依赖安装')
|
|
36
36
|
.action(async (opts) => {
|
|
37
|
-
await createWebPlatform();
|
|
37
|
+
await createWebPlatform({ framework: 'vue' });
|
|
38
38
|
const webDir = path.join(process.cwd(), 'platforms', 'vue');
|
|
39
39
|
if (!opts.skipInstall) {
|
|
40
40
|
execSync('npm install', { stdio: 'inherit', cwd: webDir });
|
|
@@ -42,6 +42,19 @@ program
|
|
|
42
42
|
execSync('npm run dev', { stdio: 'inherit', cwd: webDir });
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
+
program
|
|
46
|
+
.command('angular')
|
|
47
|
+
.description('生成 Angular 平台并在 platforms/angular 内执行构建')
|
|
48
|
+
.option('--skip-install', '跳过依赖安装')
|
|
49
|
+
.action(async (opts) => {
|
|
50
|
+
const res = await createWebPlatform({ framework: 'angular' });
|
|
51
|
+
const webDir = res?.webDir || path.join(process.cwd(), 'platforms', 'angular');
|
|
52
|
+
if (!opts.skipInstall) {
|
|
53
|
+
execSync('npm install', { stdio: 'inherit', cwd: webDir });
|
|
54
|
+
}
|
|
55
|
+
execSync('npm run build', { stdio: 'inherit', cwd: webDir });
|
|
56
|
+
});
|
|
57
|
+
|
|
45
58
|
program
|
|
46
59
|
.command('nuxt')
|
|
47
60
|
.description('生成 Nuxt 平台并在 platforms/nuxt 内执行构建')
|
|
@@ -14,52 +14,57 @@ function createWebPlatform(options = {}) {
|
|
|
14
14
|
// 当作为 node_modules 使用时,项目根应为当前工作目录
|
|
15
15
|
const projectRoot = options.projectRoot ? path.resolve(options.projectRoot) : process.cwd();
|
|
16
16
|
const platformsDir = path.join(projectRoot, 'platforms');
|
|
17
|
-
const
|
|
17
|
+
const framework = options.framework || detectFramework(projectRoot);
|
|
18
|
+
const webDir = path.join(platformsDir, framework);
|
|
18
19
|
const srcDir = path.join(projectRoot, 'src');
|
|
19
20
|
|
|
20
21
|
console.log('[web-adapter] 项目根目录:', projectRoot);
|
|
21
22
|
console.log('[web-adapter] platforms 目录:', platformsDir);
|
|
22
|
-
console.log('[web-adapter]
|
|
23
|
+
console.log('[web-adapter] framework:', framework);
|
|
24
|
+
console.log('[web-adapter] web 目录:', webDir);
|
|
23
25
|
console.log('[web-adapter] src 目录:', srcDir);
|
|
24
26
|
|
|
25
27
|
try {
|
|
26
28
|
if (!fs.existsSync(webDir)) {
|
|
27
29
|
fs.mkdirSync(webDir, { recursive: true });
|
|
28
|
-
console.log('[web-adapter] 已创建
|
|
30
|
+
console.log('[web-adapter] 已创建 web 目录');
|
|
29
31
|
} else {
|
|
30
|
-
console.log('[web-adapter]
|
|
32
|
+
console.log('[web-adapter] web 目录已存在');
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
// 1) 复制源码(仅脚本层转换,保留模板标签)
|
|
34
|
-
copy.copySourceFiles(srcDir, path.join(webDir, 'src'), transform.transformContent);
|
|
36
|
+
copy.copySourceFiles(srcDir, path.join(webDir, 'src'), (c, p) => transform.transformContent(c, p, framework));
|
|
35
37
|
console.log('[web-adapter] 已复制 src 源代码');
|
|
36
38
|
|
|
37
39
|
// 2) 复制适配器 web 组件与 composables
|
|
38
|
-
const adapterComponentsDir =
|
|
40
|
+
const adapterComponentsDir =
|
|
41
|
+
framework === 'angular' ? path.join(adapterDir, 'angular', 'components') : path.join(adapterDir, 'core', 'components');
|
|
39
42
|
const targetComponentsDir = path.join(webDir, 'src', 'components', 'websfc');
|
|
40
43
|
copy.copyAdapterComponents(adapterComponentsDir, targetComponentsDir);
|
|
41
44
|
console.log('[web-adapter] 已复制适配器组件到 websfc');
|
|
42
45
|
|
|
43
|
-
const adapterComposablesDir =
|
|
46
|
+
const adapterComposablesDir =
|
|
47
|
+
framework === 'angular' ? path.join(adapterDir, 'angular', 'composables') : path.join(adapterDir, 'core', 'composables');
|
|
44
48
|
const targetComposablesDir = path.join(webDir, 'src', 'composables', 'websfc');
|
|
45
49
|
copy.copyAdapterComposables(adapterComposablesDir, targetComposablesDir);
|
|
46
50
|
console.log('[web-adapter] 已复制适配器 composables 到 websfc');
|
|
47
51
|
|
|
48
52
|
// 3) 写入 web 项目模板(index.html、package.json、vite.config、postcss、tailwind、App.vue)
|
|
49
|
-
templates.createWebPlatformFiles(webDir);
|
|
53
|
+
templates.createWebPlatformFiles(webDir, { framework });
|
|
50
54
|
console.log('[web-adapter] 已生成 web 平台模板与配置');
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
if (framework === 'vue') {
|
|
57
|
+
const routerDirPath = path.join(webDir, 'src', 'router');
|
|
58
|
+
if (!fs.existsSync(routerDirPath)) {
|
|
59
|
+
fs.mkdirSync(routerDirPath, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
const routerContent = routerGen.buildRouterFromPages(webDir);
|
|
62
|
+
fs.writeFileSync(path.join(routerDirPath, 'index.ts'), routerContent, 'utf8');
|
|
63
|
+
console.log('[web-adapter] 已创建路由配置');
|
|
64
|
+
appPatch.patchAppForWeb(webDir);
|
|
65
|
+
} else if (framework === 'angular') {
|
|
66
|
+
appPatch.patchAngularAppForWeb(webDir);
|
|
56
67
|
}
|
|
57
|
-
const routerContent = routerGen.buildRouterFromPages(webDir);
|
|
58
|
-
fs.writeFileSync(path.join(routerDirPath, 'index.ts'), routerContent, 'utf8');
|
|
59
|
-
console.log('[web-adapter] 已创建路由配置');
|
|
60
|
-
|
|
61
|
-
// 5) 修补 app.ts:注册所有 Web 适配组件并挂载应用
|
|
62
|
-
appPatch.patchAppForWeb(webDir);
|
|
63
68
|
console.log('[web-adapter] 正在安装依赖项...');
|
|
64
69
|
|
|
65
70
|
// 如需安装依赖,可在此调用(默认跳过以加快生成速度)
|
|
@@ -67,6 +72,23 @@ function createWebPlatform(options = {}) {
|
|
|
67
72
|
} catch (err) {
|
|
68
73
|
console.error('[web-adapter] 生成 web 平台时出错:', err);
|
|
69
74
|
}
|
|
75
|
+
return { framework, webDir };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function detectFramework(projectRoot) {
|
|
79
|
+
try {
|
|
80
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
81
|
+
if (fs.existsSync(pkgPath)) {
|
|
82
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
83
|
+
const configured = pkg?.nsWeb?.framework;
|
|
84
|
+
if (configured === 'vue' || configured === 'angular') return configured;
|
|
85
|
+
|
|
86
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}), ...(pkg.peerDependencies || {}) };
|
|
87
|
+
if (deps['@nativescript/angular']) return 'angular';
|
|
88
|
+
if (deps['nativescript-vue']) return 'vue';
|
|
89
|
+
}
|
|
90
|
+
} catch {}
|
|
91
|
+
return 'vue';
|
|
70
92
|
}
|
|
71
93
|
|
|
72
94
|
module.exports = { createWebPlatform };
|
|
@@ -24,4 +24,128 @@ function patchAppForWeb(webDir) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
function patchAngularAppForWeb(webDir) {
|
|
28
|
+
try {
|
|
29
|
+
const srcDir = path.join(webDir, 'src');
|
|
30
|
+
const appDir = path.join(srcDir, 'app');
|
|
31
|
+
if (!fs.existsSync(appDir)) {
|
|
32
|
+
console.warn('[web-adapter] 未找到 src/app,跳过 Angular 修补');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const appHtml = path.join(appDir, 'app.component.html');
|
|
37
|
+
if (fs.existsSync(appHtml)) {
|
|
38
|
+
const html = fs.readFileSync(appHtml, 'utf8');
|
|
39
|
+
const next = html.replace(/<\s*page-router-outlet\s*><\s*\/\s*page-router-outlet\s*>/g, '<router-outlet></router-outlet>');
|
|
40
|
+
if (next !== html) fs.writeFileSync(appHtml, next, 'utf8');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const componentFiles = [];
|
|
44
|
+
walkFiles(appDir, (p) => {
|
|
45
|
+
if (p.endsWith('.component.ts')) componentFiles.push(p);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
for (const filePath of componentFiles) {
|
|
49
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
50
|
+
|
|
51
|
+
content = content.replace(/import\s+\{\s*PageRouterOutlet\s*\}\s+from\s+['"]@nativescript\/angular['"]\s*;?/g, "import { RouterOutlet } from '@angular/router';");
|
|
52
|
+
content = content.replace(/\bPageRouterOutlet\b/g, 'RouterOutlet');
|
|
53
|
+
content = content.replace(/<\s*page-router-outlet\b/g, '<router-outlet');
|
|
54
|
+
content = content.replace(/<\/\s*page-router-outlet\s*>/g, '</router-outlet>');
|
|
55
|
+
|
|
56
|
+
content = removeImportsFromModule(content, '@nativescript/angular');
|
|
57
|
+
|
|
58
|
+
if (/\bRouterOutlet\b/.test(content) && !/import\s+\{[^}]*\bRouterOutlet\b[^}]*\}\s+from\s+['"]@angular\/router['"]/.test(content)) {
|
|
59
|
+
content = `import { RouterOutlet } from '@angular/router';\n` + content;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (/\bNativeScriptCommonModule\b/.test(content)) {
|
|
63
|
+
if (!/from\s+['"]@angular\/common['"]/.test(content)) {
|
|
64
|
+
content = `import { CommonModule } from '@angular/common';\n` + content;
|
|
65
|
+
}
|
|
66
|
+
content = content.replace(/\bNativeScriptCommonModule\b/g, 'CommonModule');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (/\bNativeScriptRouterModule\b/.test(content)) {
|
|
70
|
+
if (!/from\s+['"]@angular\/router['"]/.test(content)) {
|
|
71
|
+
content = `import { RouterModule } from '@angular/router';\n` + content;
|
|
72
|
+
}
|
|
73
|
+
content = content.replace(/\bNativeScriptRouterModule\b/g, 'RouterModule');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const rel = toPosix(path.relative(path.dirname(filePath), path.join(srcDir, 'components', 'websfc')));
|
|
77
|
+
const importPath = rel.startsWith('.') ? rel : `./${rel}`;
|
|
78
|
+
if (!/nsWebAdapterImports/.test(content)) {
|
|
79
|
+
content = `import { imports as nsWebAdapterImports } from '${importPath}';\n` + content;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (/imports\s*:\s*\[/.test(content)) {
|
|
83
|
+
content = content.replace(/imports\s*:\s*\[([\s\S]*?)\]/m, (m, inner) => {
|
|
84
|
+
if (/\.\.\.\s*nsWebAdapterImports/.test(inner)) return m;
|
|
85
|
+
const trimmed = inner.trim();
|
|
86
|
+
const suffix = trimmed.endsWith(',') || trimmed.length === 0 ? '' : ',';
|
|
87
|
+
return `imports: [${inner}${suffix} ...nsWebAdapterImports]`;
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
content = content.replace(/schemas\s*:\s*\[/m, `imports: [...nsWebAdapterImports],\n schemas: [`);
|
|
91
|
+
if (!/imports\s*:\s*\[/.test(content)) {
|
|
92
|
+
content = content.replace(/templateUrl\s*:\s*['"][^'"]+['"]\s*,/m, (m) => `${m}\n imports: [...nsWebAdapterImports],`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (/@Component\(\{[\s\S]*?\bimports\s*:/.test(content) && !/@Component\(\{[\s\S]*?\bstandalone\s*:/.test(content)) {
|
|
97
|
+
if (/\bselector\s*:/.test(content)) {
|
|
98
|
+
content = content.replace(/(\bselector\s*:\s*['"][^'"]+['"]\s*,)/, '$1\n standalone: true,');
|
|
99
|
+
} else {
|
|
100
|
+
content = content.replace(/@Component\(\{\s*\n?/, (m) => `${m} standalone: true,\n`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error('[web-adapter] 修补 Angular 应用时出错:', err);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function removeImportsFromModule(code, moduleName) {
|
|
112
|
+
const lines = String(code || '').split(/\r?\n/);
|
|
113
|
+
const out = [];
|
|
114
|
+
for (let i = 0; i < lines.length; i++) {
|
|
115
|
+
const line = lines[i];
|
|
116
|
+
if (/^\s*import\b/.test(line)) {
|
|
117
|
+
const stmtLines = [line];
|
|
118
|
+
while (i < lines.length - 1 && !/;\s*$/.test(lines[i])) {
|
|
119
|
+
i++;
|
|
120
|
+
stmtLines.push(lines[i]);
|
|
121
|
+
}
|
|
122
|
+
const stmt = stmtLines.join('\n');
|
|
123
|
+
if (new RegExp(`from\\s+['"]${escapeRegExp(moduleName)}['"]`).test(stmt)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
out.push(...stmtLines);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
out.push(line);
|
|
130
|
+
}
|
|
131
|
+
return out.join('\n');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function walkFiles(dir, onFile) {
|
|
135
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
136
|
+
for (const entry of entries) {
|
|
137
|
+
const p = path.join(dir, entry.name);
|
|
138
|
+
if (entry.isDirectory()) walkFiles(p, onFile);
|
|
139
|
+
else onFile(p);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function toPosix(p) {
|
|
144
|
+
return p.split(path.sep).join('/');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function escapeRegExp(str) {
|
|
148
|
+
return String(str).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = { patchAppForWeb, patchAngularAppForWeb };
|
|
@@ -1,35 +1,134 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
|
|
4
|
-
function createWebPlatformFiles(webDir) {
|
|
4
|
+
function createWebPlatformFiles(webDir, options = {}) {
|
|
5
|
+
const framework = options.framework || 'vue';
|
|
5
6
|
const projectRoot = path.dirname(path.dirname(webDir));
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
|
|
8
|
+
const srcDir = path.join(webDir, 'src');
|
|
9
|
+
if (!fs.existsSync(srcDir)) fs.mkdirSync(srcDir, { recursive: true });
|
|
10
|
+
|
|
11
|
+
const packageJson = buildPackageJson(framework);
|
|
12
|
+
fs.writeFileSync(path.join(webDir, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
13
|
+
|
|
14
|
+
const indexHtml = buildIndexHtml(framework);
|
|
15
|
+
fs.writeFileSync(path.join(webDir, 'index.html'), indexHtml);
|
|
16
|
+
|
|
17
|
+
const viteConfig = buildViteConfig(framework);
|
|
18
|
+
fs.writeFileSync(path.join(webDir, 'vite.config.ts'), viteConfig);
|
|
19
|
+
|
|
20
|
+
if (framework === 'angular') {
|
|
21
|
+
writeAngularTsconfigs(webDir);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
copyOrWritePostcssAndTailwind(projectRoot, webDir, framework);
|
|
25
|
+
|
|
26
|
+
if (framework === 'vue') {
|
|
27
|
+
const appVueContent = `<template>
|
|
28
|
+
<router-view></router-view>
|
|
29
|
+
</template>`;
|
|
30
|
+
fs.writeFileSync(path.join(srcDir, 'App.vue'), appVueContent, 'utf8');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function writeAngularTsconfigs(webDir) {
|
|
35
|
+
const tsconfig = {
|
|
36
|
+
compilerOptions: {
|
|
37
|
+
target: 'ES2022',
|
|
38
|
+
useDefineForClassFields: true,
|
|
39
|
+
module: 'ESNext',
|
|
40
|
+
moduleResolution: 'Bundler',
|
|
41
|
+
experimentalDecorators: true,
|
|
42
|
+
strict: true,
|
|
43
|
+
skipLibCheck: true,
|
|
44
|
+
lib: ['ES2022', 'DOM'],
|
|
45
|
+
types: [],
|
|
15
46
|
},
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
47
|
+
angularCompilerOptions: {
|
|
48
|
+
strictTemplates: false,
|
|
49
|
+
strictInjectionParameters: false,
|
|
19
50
|
},
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
51
|
+
};
|
|
52
|
+
fs.writeFileSync(path.join(webDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
|
|
53
|
+
|
|
54
|
+
const appTsconfig = {
|
|
55
|
+
extends: './tsconfig.json',
|
|
56
|
+
include: ['src/**/*.ts'],
|
|
57
|
+
};
|
|
58
|
+
fs.writeFileSync(path.join(webDir, 'tsconfig.app.json'), JSON.stringify(appTsconfig, null, 2));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function buildPackageJson(framework) {
|
|
62
|
+
if (framework === 'angular') {
|
|
63
|
+
return {
|
|
64
|
+
name: 'ns-angular-web-platform',
|
|
65
|
+
private: true,
|
|
66
|
+
scripts: {
|
|
67
|
+
dev: 'vite',
|
|
68
|
+
build: 'vite build',
|
|
69
|
+
preview: 'vite preview',
|
|
70
|
+
},
|
|
71
|
+
dependencies: {
|
|
72
|
+
'@angular/common': '^18.2.0',
|
|
73
|
+
'@angular/core': '^18.2.0',
|
|
74
|
+
'@angular/platform-browser': '^18.2.0',
|
|
75
|
+
'@angular/router': '^18.2.0',
|
|
76
|
+
rxjs: '^7.8.1',
|
|
77
|
+
'zone.js': '^0.14.10',
|
|
78
|
+
},
|
|
79
|
+
devDependencies: {
|
|
80
|
+
'@angular/build': '^18.2.0',
|
|
81
|
+
'@analogjs/vite-plugin-angular': '^1.8.0',
|
|
82
|
+
typescript: '^5.0.0',
|
|
83
|
+
vite: '^6.0.0',
|
|
84
|
+
autoprefixer: '^10.4.16',
|
|
85
|
+
postcss: '^8.4.31',
|
|
86
|
+
tailwindcss: '^3.4.0',
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
name: 'ns-vue-web-platform',
|
|
93
|
+
private: true,
|
|
94
|
+
scripts: {
|
|
95
|
+
dev: 'vite',
|
|
96
|
+
build: 'vue-tsc --noEmit && vite build',
|
|
97
|
+
preview: 'vite preview',
|
|
98
|
+
},
|
|
99
|
+
dependencies: {
|
|
100
|
+
vue: '^3.4.0',
|
|
101
|
+
'vue-router': '^4.2.5',
|
|
102
|
+
},
|
|
103
|
+
devDependencies: {
|
|
104
|
+
'@vitejs/plugin-vue': '^5.0.0',
|
|
105
|
+
typescript: '^5.0.0',
|
|
106
|
+
vite: '^5.0.0',
|
|
107
|
+
'vue-tsc': '^2.2.12',
|
|
108
|
+
autoprefixer: '^10.4.16',
|
|
109
|
+
postcss: '^8.4.31',
|
|
110
|
+
tailwindcss: '^3.4.0',
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function buildIndexHtml(framework) {
|
|
116
|
+
if (framework === 'angular') {
|
|
117
|
+
return `<!DOCTYPE html>
|
|
118
|
+
<html lang="en">
|
|
119
|
+
<head>
|
|
120
|
+
<meta charset="UTF-8" />
|
|
121
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
122
|
+
<title>NS Angular Web</title>
|
|
123
|
+
</head>
|
|
124
|
+
<body>
|
|
125
|
+
<ns-app></ns-app>
|
|
126
|
+
<script type="module" src="/src/main.ts"></script>
|
|
127
|
+
</body>
|
|
128
|
+
</html>`;
|
|
129
|
+
}
|
|
30
130
|
|
|
31
|
-
|
|
32
|
-
fs.writeFileSync(path.join(webDir, 'index.html'), `<!DOCTYPE html>
|
|
131
|
+
return `<!DOCTYPE html>
|
|
33
132
|
<html lang="en">
|
|
34
133
|
<head>
|
|
35
134
|
<meta charset="UTF-8" />
|
|
@@ -40,34 +139,32 @@ function createWebPlatformFiles(webDir) {
|
|
|
40
139
|
<div id="app"></div>
|
|
41
140
|
<script type="module" src="/src/app.ts"></script>
|
|
42
141
|
</body>
|
|
43
|
-
</html
|
|
142
|
+
</html>`;
|
|
143
|
+
}
|
|
44
144
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
devDependencies: {
|
|
58
|
-
"@vitejs/plugin-vue": "^5.0.0",
|
|
59
|
-
"typescript": "^5.0.0",
|
|
60
|
-
"vite": "^5.0.0",
|
|
61
|
-
"autoprefixer": "^10.4.16",
|
|
62
|
-
"postcss": "^8.4.31",
|
|
63
|
-
"tailwindcss": "^3.4.0"
|
|
145
|
+
function buildViteConfig(framework) {
|
|
146
|
+
if (framework === 'angular') {
|
|
147
|
+
return `import { defineConfig } from 'vite';
|
|
148
|
+
import path from 'path';
|
|
149
|
+
|
|
150
|
+
export default defineConfig(async () => {
|
|
151
|
+
const { default: angular } = await import('@analogjs/vite-plugin-angular');
|
|
152
|
+
return {
|
|
153
|
+
plugins: [angular()],
|
|
154
|
+
resolve: {
|
|
155
|
+
alias: {
|
|
156
|
+
'@': path.resolve(__dirname, './src')
|
|
64
157
|
}
|
|
158
|
+
},
|
|
159
|
+
server: {
|
|
160
|
+
port: 3006,
|
|
161
|
+
strictPort: false
|
|
162
|
+
}
|
|
65
163
|
};
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const viteConfig = `import { defineConfig } from 'vite';
|
|
164
|
+
});`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return `import { defineConfig } from 'vite';
|
|
71
168
|
import vue from '@vitejs/plugin-vue';
|
|
72
169
|
import path from 'path';
|
|
73
170
|
|
|
@@ -83,48 +180,28 @@ export default defineConfig({
|
|
|
83
180
|
strictPort: false
|
|
84
181
|
}
|
|
85
182
|
});`;
|
|
86
|
-
|
|
183
|
+
}
|
|
87
184
|
|
|
88
|
-
|
|
185
|
+
function copyOrWritePostcssAndTailwind(projectRoot, webDir, framework) {
|
|
89
186
|
const rootPostcss = path.join(projectRoot, 'postcss.config.js');
|
|
90
187
|
const rootTailwind = path.join(projectRoot, 'tailwind.config.js');
|
|
188
|
+
|
|
189
|
+
const defaultPostcss = `module.exports = {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n};`;
|
|
190
|
+
const defaultTailwind = `/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n content: [\n \"./index.html\",\n \"./src/**/*.{${framework === 'angular' ? 'html,ts,js,jsx,tsx' : 'vue,js,ts,jsx,tsx'}}\",\n ],\n theme: {\n extend: {},\n },\n plugins: [],\n};`;
|
|
191
|
+
|
|
91
192
|
try {
|
|
92
|
-
if (fs.existsSync(rootPostcss))
|
|
93
|
-
|
|
94
|
-
} else {
|
|
95
|
-
const postcssConfig = `module.exports = {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n};`;
|
|
96
|
-
fs.writeFileSync(path.join(webDir, 'postcss.config.js'), postcssConfig);
|
|
97
|
-
}
|
|
193
|
+
if (fs.existsSync(rootPostcss)) fs.copyFileSync(rootPostcss, path.join(webDir, 'postcss.config.js'));
|
|
194
|
+
else fs.writeFileSync(path.join(webDir, 'postcss.config.js'), defaultPostcss);
|
|
98
195
|
} catch (e) {
|
|
99
|
-
|
|
100
|
-
const postcssConfig = `module.exports = {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n};`;
|
|
101
|
-
fs.writeFileSync(path.join(webDir, 'postcss.config.js'), postcssConfig);
|
|
196
|
+
fs.writeFileSync(path.join(webDir, 'postcss.config.js'), defaultPostcss);
|
|
102
197
|
}
|
|
198
|
+
|
|
103
199
|
try {
|
|
104
|
-
if (fs.existsSync(rootTailwind))
|
|
105
|
-
|
|
106
|
-
} else {
|
|
107
|
-
const tailwindConfig = `/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n content: [\n "./index.html",\n "./src/**/*.{vue,js,ts,jsx,tsx}",\n ],\n theme: {\n extend: {},\n },\n plugins: [],\n};`;
|
|
108
|
-
fs.writeFileSync(path.join(webDir, 'tailwind.config.js'), tailwindConfig);
|
|
109
|
-
}
|
|
200
|
+
if (fs.existsSync(rootTailwind)) fs.copyFileSync(rootTailwind, path.join(webDir, 'tailwind.config.js'));
|
|
201
|
+
else fs.writeFileSync(path.join(webDir, 'tailwind.config.js'), defaultTailwind);
|
|
110
202
|
} catch (e) {
|
|
111
|
-
|
|
112
|
-
const tailwindConfig = `/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n content: [\n "./index.html",\n "./src/**/*.{vue,js,ts,jsx,tsx}",\n ],\n theme: {\n extend: {},\n },\n plugins: [],\n};`;
|
|
113
|
-
fs.writeFileSync(path.join(webDir, 'tailwind.config.js'), tailwindConfig);
|
|
203
|
+
fs.writeFileSync(path.join(webDir, 'tailwind.config.js'), defaultTailwind);
|
|
114
204
|
}
|
|
115
|
-
|
|
116
|
-
// App.vue(根组件占位,仅渲染 <router-view/>)
|
|
117
|
-
const appVueContent = `<template>
|
|
118
|
-
<router-view></router-view>
|
|
119
|
-
</template>
|
|
120
|
-
|
|
121
|
-
<script setup lang="ts">
|
|
122
|
-
// App root component
|
|
123
|
-
</script>`;
|
|
124
|
-
const appVuePath = path.join(webDir, 'src', 'App.vue');
|
|
125
|
-
const srcDir = path.join(webDir, 'src');
|
|
126
|
-
if (!fs.existsSync(srcDir)) fs.mkdirSync(srcDir, { recursive: true });
|
|
127
|
-
fs.writeFileSync(appVuePath, appVueContent, 'utf8');
|
|
128
205
|
}
|
|
129
206
|
|
|
130
|
-
module.exports = { createWebPlatformFiles };
|
|
207
|
+
module.exports = { createWebPlatformFiles };
|