@neuxnet/neux-cli 0.2.3 → 0.2.5
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 +10 -0
- package/README.zh-CN.md +10 -0
- package/assets/container/assets/pageFrame.js +6 -6
- package/assets/container/assets/service.js +2 -2
- package/assets/init/neux-components.code-snippets +756 -0
- package/assets/init/neux-html-data.json +3345 -0
- package/assets/init/schemas/app.schema.json +63 -0
- package/assets/init/schemas/neux-config.schema.json +18 -0
- package/assets/init/schemas/page.schema.json +20 -0
- package/assets/init/schemas/project-config.schema.json +23 -0
- package/assets/init/schemas/project-private-config.schema.json +161 -0
- package/package.json +11 -2
- package/scripts/smoke-init-types.mjs +114 -0
- package/scripts/verify-json-schemas.mjs +40 -0
- package/src/bin/cli.js +12 -0
- package/src/core/compiler.js +11 -0
- package/src/core/init.js +88 -16
- package/src/core/package-info.js +1 -0
- package/src/core/project.js +138 -1
- package/src/index.js +1 -1
package/src/core/init.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'node:path'
|
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
4
|
import { DiminaCliError } from './errors.js'
|
|
5
5
|
import { pathExists, resolvePath, writeJson } from './fs.js'
|
|
6
|
-
import {
|
|
6
|
+
import { getCliPackageInfo } from './package-info.js'
|
|
7
7
|
import { saveCliKey } from '../providers/service-config.js'
|
|
8
8
|
import { promptLines, shouldPrompt } from './prompts.js'
|
|
9
9
|
|
|
@@ -12,7 +12,6 @@ const DEFAULT_LIB_VERSION = '3.3.3'
|
|
|
12
12
|
export const DEFAULT_SERVER_URL = 'https://demo-c.paas.superapp.neuvision.cn'
|
|
13
13
|
const CLI_PACKAGE_NAME = '@neuxnet/neux-cli'
|
|
14
14
|
const TEMPLATE_ASSET_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../assets/init')
|
|
15
|
-
const DEFAULT_DOCS_BASE_URL = `${DEFAULT_SERVER_URL}/miniappdoc`
|
|
16
15
|
|
|
17
16
|
export async function initProject(options = {}) {
|
|
18
17
|
const targetArg = options._?.[0] || options.project || '.'
|
|
@@ -25,8 +24,14 @@ export async function initProject(options = {}) {
|
|
|
25
24
|
const appId = options.appId || prompts.appId || DEFAULT_APP_ID
|
|
26
25
|
const serverUrl = options.serverUrl || prompts.serverUrl || DEFAULT_SERVER_URL
|
|
27
26
|
const cliKey = options.key || prompts.cliKey
|
|
28
|
-
const
|
|
29
|
-
const files = createTemplateFiles({
|
|
27
|
+
const cliPackage = await getCliPackageInfo()
|
|
28
|
+
const files = createTemplateFiles({
|
|
29
|
+
appId,
|
|
30
|
+
projectName,
|
|
31
|
+
serverUrl,
|
|
32
|
+
cliVersion: cliPackage.version,
|
|
33
|
+
typesPackage: cliPackage.typesPackage,
|
|
34
|
+
})
|
|
30
35
|
|
|
31
36
|
for (const file of files) {
|
|
32
37
|
const filePath = path.join(projectPath, file.path)
|
|
@@ -36,9 +41,7 @@ export async function initProject(options = {}) {
|
|
|
36
41
|
await fs.mkdir(path.dirname(filePath), { recursive: true })
|
|
37
42
|
const sourcePath = path.join(TEMPLATE_ASSET_DIR, file.source)
|
|
38
43
|
if (file.source === 'types/neux-api.d.ts') {
|
|
39
|
-
|
|
40
|
-
const content = await fs.readFile(sourcePath, 'utf8')
|
|
41
|
-
await fs.writeFile(filePath, content.replaceAll(DEFAULT_DOCS_BASE_URL, docsBaseUrl), 'utf8')
|
|
44
|
+
await fs.writeFile(filePath, `/// <reference types="${file.typesPackage.name}" />\n`, 'utf8')
|
|
42
45
|
}
|
|
43
46
|
else await fs.copyFile(sourcePath, filePath)
|
|
44
47
|
}
|
|
@@ -136,13 +139,45 @@ async function mergeJsonFile(filePath, generated) {
|
|
|
136
139
|
...(existing['files.associations'] || {}),
|
|
137
140
|
}
|
|
138
141
|
}
|
|
142
|
+
if (generated?.['json.schemas'] || existing?.['json.schemas']) {
|
|
143
|
+
const generatedSchemas = Array.isArray(generated?.['json.schemas']) ? generated['json.schemas'] : []
|
|
144
|
+
const existingSchemas = Array.isArray(existing?.['json.schemas']) ? existing['json.schemas'] : []
|
|
145
|
+
const keys = new Set()
|
|
146
|
+
merged['json.schemas'] = [...generatedSchemas, ...existingSchemas].filter(schema => {
|
|
147
|
+
const key = `${schema?.url || ''}|${JSON.stringify(schema?.fileMatch || [])}`
|
|
148
|
+
if (keys.has(key)) return false
|
|
149
|
+
keys.add(key)
|
|
150
|
+
return true
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
if (generated?.['html.customData'] || existing?.['html.customData']) {
|
|
154
|
+
merged['html.customData'] = [
|
|
155
|
+
...new Set([
|
|
156
|
+
...(Array.isArray(generated?.['html.customData']) ? generated['html.customData'] : []),
|
|
157
|
+
...(Array.isArray(existing?.['html.customData']) ? existing['html.customData'] : []),
|
|
158
|
+
]),
|
|
159
|
+
]
|
|
160
|
+
}
|
|
139
161
|
if (filePath.endsWith('neux.code-snippets')) {
|
|
140
162
|
Object.assign(merged, generated || {}, existing || {})
|
|
141
163
|
}
|
|
142
164
|
await writeJson(filePath, merged)
|
|
143
165
|
}
|
|
144
166
|
|
|
145
|
-
function createTemplateFiles({
|
|
167
|
+
function createTemplateFiles({
|
|
168
|
+
appId,
|
|
169
|
+
projectName,
|
|
170
|
+
serverUrl = DEFAULT_SERVER_URL,
|
|
171
|
+
cliVersion,
|
|
172
|
+
typesPackage,
|
|
173
|
+
}) {
|
|
174
|
+
if (!typesPackage?.name || !typesPackage?.versionRange) {
|
|
175
|
+
throw new DiminaCliError(
|
|
176
|
+
'NEUX_CLI_TYPES_PACKAGE_METADATA_MISSING',
|
|
177
|
+
'The CLI package does not declare a compatible miniapp types package.',
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
146
181
|
return [
|
|
147
182
|
{
|
|
148
183
|
path: 'project.config.json',
|
|
@@ -198,6 +233,7 @@ function createTemplateFiles({ appId, projectName, serverUrl = DEFAULT_SERVER_UR
|
|
|
198
233
|
},
|
|
199
234
|
devDependencies: {
|
|
200
235
|
[CLI_PACKAGE_NAME]: `^${cliVersion}`,
|
|
236
|
+
[typesPackage.name]: typesPackage.versionRange,
|
|
201
237
|
},
|
|
202
238
|
},
|
|
203
239
|
},
|
|
@@ -260,8 +296,21 @@ function createTemplateFiles({ appId, projectName, serverUrl = DEFAULT_SERVER_UR
|
|
|
260
296
|
'*.nxml': 'html',
|
|
261
297
|
'*.nxss': 'css',
|
|
262
298
|
},
|
|
299
|
+
'json.schemas': createJsonSchemaSettings(),
|
|
300
|
+
'html.customData': ['./.vscode/neux-html-data.json'],
|
|
263
301
|
},
|
|
264
302
|
},
|
|
303
|
+
{
|
|
304
|
+
path: '.vscode/neux-html-data.json',
|
|
305
|
+
kind: 'asset',
|
|
306
|
+
source: 'neux-html-data.json',
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
path: '.vscode/neux-components.code-snippets',
|
|
310
|
+
kind: 'asset',
|
|
311
|
+
source: 'neux-components.code-snippets',
|
|
312
|
+
},
|
|
313
|
+
...createJsonSchemaFiles(),
|
|
265
314
|
{
|
|
266
315
|
path: '.vscode/neux.code-snippets',
|
|
267
316
|
kind: 'merge-json',
|
|
@@ -276,11 +325,17 @@ function createTemplateFiles({ appId, projectName, serverUrl = DEFAULT_SERVER_UR
|
|
|
276
325
|
checkJs: false,
|
|
277
326
|
noEmit: true,
|
|
278
327
|
target: 'ES2020',
|
|
328
|
+
types: [typesPackage.name],
|
|
279
329
|
},
|
|
280
330
|
include: ['**/*.js', '**/*.ts', 'types/**/*.d.ts'],
|
|
281
331
|
},
|
|
282
332
|
},
|
|
283
|
-
{
|
|
333
|
+
{
|
|
334
|
+
path: 'types/neux-api.d.ts',
|
|
335
|
+
kind: 'asset',
|
|
336
|
+
source: 'types/neux-api.d.ts',
|
|
337
|
+
typesPackage,
|
|
338
|
+
},
|
|
284
339
|
{ path: 'assets/tabbar/home.png', kind: 'asset', source: 'tabbar/home.png' },
|
|
285
340
|
{ path: 'assets/tabbar/home-active.png', kind: 'asset', source: 'tabbar/home-active.png' },
|
|
286
341
|
{ path: 'assets/tabbar/list.png', kind: 'asset', source: 'tabbar/list.png' },
|
|
@@ -522,6 +577,30 @@ function createTemplateFiles({ appId, projectName, serverUrl = DEFAULT_SERVER_UR
|
|
|
522
577
|
]
|
|
523
578
|
}
|
|
524
579
|
|
|
580
|
+
function createJsonSchemaSettings() {
|
|
581
|
+
return [
|
|
582
|
+
{ fileMatch: ['/app.json'], url: './.vscode/neux-json-schemas/app.schema.json' },
|
|
583
|
+
{ fileMatch: ['/pages/**/*.json', '/components/**/*.json'], url: './.vscode/neux-json-schemas/page.schema.json' },
|
|
584
|
+
{ fileMatch: ['/project.config.json'], url: './.vscode/neux-json-schemas/project-config.schema.json' },
|
|
585
|
+
{ fileMatch: ['/project.private.config.json'], url: './.vscode/neux-json-schemas/project-private-config.schema.json' },
|
|
586
|
+
{ fileMatch: ['/neux.config.json'], url: './.vscode/neux-json-schemas/neux-config.schema.json' },
|
|
587
|
+
]
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function createJsonSchemaFiles() {
|
|
591
|
+
return [
|
|
592
|
+
'app.schema.json',
|
|
593
|
+
'page.schema.json',
|
|
594
|
+
'project-config.schema.json',
|
|
595
|
+
'project-private-config.schema.json',
|
|
596
|
+
'neux-config.schema.json',
|
|
597
|
+
].map(name => ({
|
|
598
|
+
path: `.vscode/neux-json-schemas/${name}`,
|
|
599
|
+
kind: 'asset',
|
|
600
|
+
source: `schemas/${name}`,
|
|
601
|
+
}))
|
|
602
|
+
}
|
|
603
|
+
|
|
525
604
|
function escapeJsString(value) {
|
|
526
605
|
return String(value).replaceAll('\\', '\\\\').replaceAll("'", "\\'")
|
|
527
606
|
}
|
|
@@ -759,13 +838,6 @@ function createNeuxCodeSnippets() {
|
|
|
759
838
|
body: ['<view nx:if="{{${1:visible}}}">', ' ${0}', '</view>'],
|
|
760
839
|
description: 'Render a view conditionally',
|
|
761
840
|
},
|
|
762
|
-
'Tap Button': {
|
|
763
|
-
prefix: 'nxbutton',
|
|
764
|
-
scope: 'html',
|
|
765
|
-
include: ['**/*.nxml'],
|
|
766
|
-
body: ['<button bindtap="${1:handleTap}">${2:Button}</button>$0'],
|
|
767
|
-
description: 'Insert a button with a tap handler',
|
|
768
|
-
},
|
|
769
841
|
'Page View': {
|
|
770
842
|
prefix: 'nxview',
|
|
771
843
|
scope: 'html',
|
package/src/core/package-info.js
CHANGED
package/src/core/project.js
CHANGED
|
@@ -27,9 +27,16 @@ export async function inspectProject(options = {}) {
|
|
|
27
27
|
const appName = options.name || effectiveProjectConfig.projectname || effectiveProjectConfig.projectName || `App ${appId || 'unknown'}`
|
|
28
28
|
const entryPath = options.entryPath || appConfig.entryPagePath || pages[0] || 'pages/index'
|
|
29
29
|
const hasPrivateConfig = await pathExists(projectPrivateConfigPath)
|
|
30
|
+
const diagnostics = await validateProjectConfiguration({
|
|
31
|
+
projectPath,
|
|
32
|
+
miniprogramRoot,
|
|
33
|
+
sourceRoot,
|
|
34
|
+
appConfig,
|
|
35
|
+
pages,
|
|
36
|
+
})
|
|
30
37
|
|
|
31
38
|
return {
|
|
32
|
-
ok:
|
|
39
|
+
ok: diagnostics.every(item => item.severity !== 'error'),
|
|
33
40
|
command: 'inspect',
|
|
34
41
|
projectPath,
|
|
35
42
|
sourceRoot,
|
|
@@ -48,7 +55,137 @@ export async function inspectProject(options = {}) {
|
|
|
48
55
|
condition: effectiveProjectConfig.condition || {},
|
|
49
56
|
neuxCli: effectiveProjectConfig.neuxCli || {},
|
|
50
57
|
cliConfig,
|
|
58
|
+
diagnostics,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function validateProjectConfiguration({ projectPath, miniprogramRoot = '', sourceRoot, appConfig = {}, pages = [] } = {}) {
|
|
63
|
+
const diagnostics = []
|
|
64
|
+
projectPath ||= sourceRoot
|
|
65
|
+
const add = (severity, code, message, pathName, details = {}) => {
|
|
66
|
+
diagnostics.push({ severity, code, message, path: pathName, ...details })
|
|
67
|
+
}
|
|
68
|
+
const pageSet = new Set(pages.map(normalizeProjectPath))
|
|
69
|
+
const entryPagePath = normalizeProjectPath(appConfig.entryPagePath || pages[0])
|
|
70
|
+
|
|
71
|
+
if (!sourceRoot) {
|
|
72
|
+
add('error', 'NEUX_SOURCE_ROOT_INVALID', 'A source root is required for project validation', 'project.config.json')
|
|
73
|
+
return diagnostics
|
|
74
|
+
}
|
|
75
|
+
if (miniprogramRoot && path.isAbsolute(String(miniprogramRoot))) {
|
|
76
|
+
add('error', 'NEUX_MINIPROGRAM_ROOT_INVALID', 'miniprogramRoot must be a relative path', 'project.config.json', { value: miniprogramRoot })
|
|
77
|
+
}
|
|
78
|
+
const normalizedRoot = normalizeProjectPath(miniprogramRoot)
|
|
79
|
+
if (normalizedRoot && !isWithinProjectRoot(projectPath, sourceRoot)) {
|
|
80
|
+
add('error', 'NEUX_MINIPROGRAM_ROOT_OUTSIDE_PROJECT', 'miniprogramRoot resolves outside the project directory', 'project.config.json', { value: miniprogramRoot })
|
|
81
|
+
}
|
|
82
|
+
if (normalizedRoot && !(await pathExists(sourceRoot))) {
|
|
83
|
+
add('error', 'NEUX_MINIPROGRAM_ROOT_NOT_FOUND', `miniprogramRoot does not exist: ${miniprogramRoot}`, 'project.config.json', { value: miniprogramRoot })
|
|
84
|
+
}
|
|
85
|
+
if (appConfig.entryPagePath && !pageSet.has(entryPagePath)) {
|
|
86
|
+
add('error', 'NEUX_ENTRY_PAGE_NOT_DECLARED', `Entry page is not declared in pages: ${appConfig.entryPagePath}`, 'app.json', { value: appConfig.entryPagePath })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (const page of pages) {
|
|
90
|
+
const normalized = normalizeProjectPath(page)
|
|
91
|
+
if (!(await hasAnyProjectFile(sourceRoot, normalized, ['.json', '.nxml', '.nxss', '.js', '.ts', '.wxml', '.wxss']))) {
|
|
92
|
+
add('error', 'NEUX_PAGE_NOT_FOUND', `Page does not exist: ${page}`, 'app.json', { value: page })
|
|
93
|
+
continue
|
|
94
|
+
}
|
|
95
|
+
const pageConfigPath = path.join(sourceRoot, `${normalized}.json`)
|
|
96
|
+
const pageConfig = await readJson(pageConfigPath, {})
|
|
97
|
+
await validateUsingComponents(pageConfig.usingComponents, path.dirname(pageConfigPath), sourceRoot, `${page}.json`, add)
|
|
51
98
|
}
|
|
99
|
+
|
|
100
|
+
const tabItems = Array.isArray(appConfig.tabBar?.list) ? appConfig.tabBar.list : []
|
|
101
|
+
if (appConfig.tabBar && !Array.isArray(appConfig.tabBar.list)) {
|
|
102
|
+
add('error', 'NEUX_TABBAR_LIST_INVALID', 'tabBar.list must be an array', 'app.json')
|
|
103
|
+
}
|
|
104
|
+
if (tabItems.length > 5) add('error', 'NEUX_TABBAR_TOO_MANY_ITEMS', 'tabBar.list cannot contain more than 5 items', 'app.json', { count: tabItems.length })
|
|
105
|
+
if (appConfig.tabBar && tabItems.length === 0) add('error', 'NEUX_TABBAR_LIST_EMPTY', 'tabBar.list must contain at least one item', 'app.json')
|
|
106
|
+
for (const [index, item] of tabItems.entries()) {
|
|
107
|
+
const pagePath = normalizeProjectPath(item?.pagePath)
|
|
108
|
+
if (!pagePath) {
|
|
109
|
+
add('error', 'NEUX_TABBAR_PAGE_INVALID', `tabBar.list[${index}].pagePath must be a non-empty string`, 'app.json')
|
|
110
|
+
} else if (!pageSet.has(pagePath)) {
|
|
111
|
+
add('error', 'NEUX_TABBAR_PAGE_NOT_DECLARED', `TabBar page is not declared in pages: ${item.pagePath}`, 'app.json', { value: item.pagePath })
|
|
112
|
+
}
|
|
113
|
+
for (const field of ['iconPath', 'selectedIconPath']) {
|
|
114
|
+
if (item?.[field] && !(await pathExists(path.resolve(sourceRoot, item[field])))) {
|
|
115
|
+
add('error', 'NEUX_ASSET_NOT_FOUND', `TabBar asset does not exist: ${item[field]}`, 'app.json', { value: item[field], field })
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await validateUsingComponents(appConfig.usingComponents, sourceRoot, sourceRoot, 'app.json', add)
|
|
121
|
+
const subPackages = appConfig.subPackages || appConfig.subpackages
|
|
122
|
+
if (Array.isArray(subPackages)) {
|
|
123
|
+
const roots = new Set()
|
|
124
|
+
for (const [index, subPackage] of subPackages.entries()) {
|
|
125
|
+
const root = normalizeProjectPath(subPackage?.root)
|
|
126
|
+
if (!root) {
|
|
127
|
+
add('error', 'NEUX_SUBPACKAGE_ROOT_INVALID', `subPackages[${index}].root must be a non-empty string`, 'app.json')
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
if (roots.has(root)) add('error', 'NEUX_SUBPACKAGE_ROOT_DUPLICATE', `Duplicate subpackage root: ${root}`, 'app.json', { value: root })
|
|
131
|
+
roots.add(root)
|
|
132
|
+
for (const page of Array.isArray(subPackage.pages) ? subPackage.pages : []) {
|
|
133
|
+
const pagePath = normalizeProjectPath(path.posix.join(root, page))
|
|
134
|
+
if (pageSet.has(pagePath)) {
|
|
135
|
+
add('error', 'NEUX_PAGE_IN_MULTIPLE_PACKAGES', `Page is declared in both the main package and a subpackage: ${pagePath}`, 'app.json', { value: pagePath })
|
|
136
|
+
}
|
|
137
|
+
if (!(await hasAnyProjectFile(sourceRoot, pagePath, ['.json', '.nxml', '.nxss', '.js', '.ts', '.wxml', '.wxss']))) {
|
|
138
|
+
add('error', 'NEUX_SUBPACKAGE_PAGE_NOT_FOUND', `Subpackage page does not exist: ${pagePath}`, 'app.json', { value: pagePath })
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const rootEntries = [...roots]
|
|
143
|
+
for (let i = 0; i < rootEntries.length; i += 1) {
|
|
144
|
+
for (let j = i + 1; j < rootEntries.length; j += 1) {
|
|
145
|
+
if (rootEntries[i].startsWith(`${rootEntries[j]}/`) || rootEntries[j].startsWith(`${rootEntries[i]}/`)) {
|
|
146
|
+
add('error', 'NEUX_SUBPACKAGE_ROOT_OVERLAP', `Subpackage roots overlap: ${rootEntries[i]} and ${rootEntries[j]}`, 'app.json')
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return diagnostics
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function validateUsingComponents(usingComponents, ownerDir, sourceRoot, configPath, add) {
|
|
155
|
+
if (!usingComponents || typeof usingComponents !== 'object' || Array.isArray(usingComponents)) return
|
|
156
|
+
for (const [name, target] of Object.entries(usingComponents)) {
|
|
157
|
+
if (typeof target !== 'string' || !target.trim()) {
|
|
158
|
+
add('error', 'NEUX_COMPONENT_PATH_INVALID', `Component path must be a non-empty string: ${name}`, configPath, { value: target })
|
|
159
|
+
continue
|
|
160
|
+
}
|
|
161
|
+
if (target.startsWith('plugin://') || target.startsWith('npm:') || (!target.startsWith('.') && !target.startsWith('/'))) continue
|
|
162
|
+
const normalized = target.startsWith('/') ? target.slice(1) : path.relative(sourceRoot, path.resolve(ownerDir, target))
|
|
163
|
+
if (!(await hasAnyProjectFile(sourceRoot, normalizeProjectPath(normalized), ['.json', '.nxml', '.nxss', '.js', '.ts', '.wxml', '.wxss']))) {
|
|
164
|
+
add('error', 'NEUX_COMPONENT_NOT_FOUND', `Component does not exist: ${target}`, configPath, { value: target, component: name })
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function hasAnyProjectFile(sourceRoot, basePath, extensions) {
|
|
170
|
+
const candidates = [basePath]
|
|
171
|
+
const basename = path.posix.basename(basePath)
|
|
172
|
+
candidates.push(path.posix.join(basePath, basename))
|
|
173
|
+
for (const candidate of candidates) {
|
|
174
|
+
for (const extension of extensions) {
|
|
175
|
+
if (await pathExists(path.join(sourceRoot, `${candidate}${extension}`))) return true
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return false
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function normalizeProjectPath(value) {
|
|
182
|
+
return String(value || '').replaceAll('\\', '/').replace(/^\/+|\/+$/g, '')
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function isWithinProjectRoot(projectPath, sourceRoot) {
|
|
186
|
+
const root = path.resolve(projectPath || sourceRoot)
|
|
187
|
+
const candidate = path.resolve(sourceRoot)
|
|
188
|
+
return candidate === root || candidate.startsWith(`${root}${path.sep}`)
|
|
52
189
|
}
|
|
53
190
|
|
|
54
191
|
function mergeProjectConfig(projectConfig = {}, privateConfig = {}) {
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { buildProject, buildStyleOnlyProject, buildViewOnlyProject, buildLogicOnlyProject } from './core/compiler.js'
|
|
2
2
|
export { DEFAULT_SERVER_URL, initProject } from './core/init.js'
|
|
3
|
-
export { inspectProject } from './core/project.js'
|
|
3
|
+
export { inspectProject, validateProjectConfiguration } from './core/project.js'
|
|
4
4
|
export { packProject } from './core/pack.js'
|
|
5
5
|
export { startPreview } from './core/preview-server.js'
|
|
6
6
|
export { startDev } from './core/dev.js'
|