Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 92 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function loadPagesJson(path = 'src/pages.json', cwd = process.cwd()) {
encoding: 'utf-8',
})

const { pages = [], subPackages = [] } = jsonParse(pagesJsonRaw)
const { pages = [], subPackages = [] } = parsePagesJson(pagesJsonRaw, process.env.UNI_PLATFORM || '')

return [
...pages,
Expand All @@ -44,6 +44,97 @@ export function loadPagesJson(path = 'src/pages.json', cwd = process.cwd()) {
]
}

/**
* parsePagesJson
* The `parsePagesJson` exported by `@dcloudio/uni-cli-shared` does not support parsing `subPackage` in the miniProgram environment.
* Therefore, a custom parsePagesJson implementation is provided here.
* @param content The content of the pages.json file.
* @param platform The platform to target.
* @returns The parsed pages.json object.
*/
export function parsePagesJson(content: string, platform: string) {
try {
const preprocessed = preprocess(content, platform)
return jsonParse(preprocessed)
}
catch {
return {}
}
}

function getPlatformContext(platform: string): Record<string, boolean> {
const ctx: Record<string, boolean> = {
VUE3: true,
}
const p = platform.toUpperCase()
if (p) {
ctx[p] = true
if (p.startsWith('APP-'))
ctx.APP = true

if (p.startsWith('MP-'))
ctx.MP = true

if (p === 'H5' || p === 'WEB') {
ctx.H5 = true
ctx.WEB = true
}
}
return ctx
}

function evaluate(condition: string, context: Record<string, boolean>): boolean {
const code = condition.replace(/[a-zA-Z0-9_$-]+/g, (match) => {
if (match === 'true' || match === 'false')
return match
return context[match] ? 'true' : 'false'
})
try {
// eslint-disable-next-line no-new-func
return new Function(`return !!(${code})`)()
}
catch {
return false
}
}

function preprocess(content: string, platform: string) {
const context = getPlatformContext(platform)
const lines = content.split('\n')
const result: string[] = []
const stack: boolean[] = []

const shouldInclude = () => stack.every(v => v)

for (const line of lines) {
const trimmed = line.trim()
if (trimmed.startsWith('//')) {
const comment = trimmed.substring(2).trim()
if (comment.startsWith('#ifdef')) {
stack.push(evaluate(comment.substring(6).trim(), context))
result.push('')
continue
}
if (comment.startsWith('#ifndef')) {
stack.push(!evaluate(comment.substring(7).trim(), context))
result.push('')
continue
}
if (comment.startsWith('#endif')) {
stack.pop()
result.push('')
continue
}
}

if (shouldInclude())
result.push(line)
else
result.push('')
}
return result.join('\n')
}
Comment on lines +101 to +136
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

uni-app conditional compilation #else #elif directives syntax

💡 Result:

Yes — uni-app conditional compilation uses comment-style directives similar to C. Use // (JS), /* */ (CSS), or (Vue templates) and the directives #ifdef, #ifndef, #elif, #else and end with #endif.

Syntax (JS example):
// #ifdef APP-PLUS
// code for App
// #elif H5
// code for H5
// #else
// fallback code for other platforms
// #endif

Notes:

  • Multiple platforms can be combined with || (e.g. APP-PLUS||H5).
  • Use /* ... */ for styles and inside templates.
  • Valid platform identifiers and supported files are listed in the uni-app docs. [1][2]

Sources:
[1] uni-app — Conditional compilation / Cross-platform docs.
[2] Community guide / examples showing #elif usage in uni-app.


🏁 Script executed:

# Check the actual implementation in src/utils.ts
cat -n src/utils.ts | sed -n '101,136p'

Repository: uni-helper/vite-plugin-uni-layouts

Length of output: 1278


🏁 Script executed:

# Search for test files related to preprocess function
fd -e test.ts -e spec.ts -e test.js -e spec.js | head -20

Repository: uni-helper/vite-plugin-uni-layouts

Length of output: 200


🏁 Script executed:

# Search for any existing `#else` or `#elif` handling in the codebase
rg -i '#else|#elif' --type ts --type js

Repository: uni-helper/vite-plugin-uni-layouts

Length of output: 60


🏁 Script executed:

# Search for other references to the preprocess function
rg 'preprocess' --type ts --type js -B 2 -A 2

Repository: uni-helper/vite-plugin-uni-layouts

Length of output: 546


🏁 Script executed:

# Search for tests specifically for the preprocess function
rg 'preprocess' test/ --type ts --type js -B 5 -A 10

Repository: uni-helper/vite-plugin-uni-layouts

Length of output: 60


Add support for #elif and #else directives to match uni-app's conditional compilation syntax.

The uni-app framework officially supports #elif and #else directives alongside #ifdef, #ifndef, and #endif. The current implementation only handles #ifdef, #ifndef, and #endif, which prevents developers from using the full conditional compilation feature set documented in uni-app's specs.

Additionally, unbalanced #ifdef/#endif pairs are silently ignored. Add validation to warn or error when stack is non-empty after processing all lines, indicating unclosed directives.

🤖 Prompt for AI Agents
In `@src/utils.ts` around lines 101 - 136, Update preprocess to support uni-app
conditional directives by extending the comment-handling branch in function
preprocess: add handling for '#else' (flip the top of stack to the inverse of
previous condition evaluation for that block) and '#elif' (evaluate the new
condition with evaluate(..., context) and replace the top of the stack with that
result, ensuring only one active branch per conditional chain); ensure
shouldInclude logic remains stack.every(v => v) to respect nested results. Also
add validation after the loop to check if stack.length !== 0 and emit a clear
warning or throw an error (using console.warn/console.error or a provided
logger) indicating unclosed directives so unbalanced `#ifdef/`#endif pairs are not
silently ignored. Make sure to reference and use getPlatformContext and evaluate
consistently and preserve existing behavior of pushing blank lines into result
for directive lines.


export function getTarget(
resolvePath: string,
pages: Page[] = [],
Expand Down
109 changes: 107 additions & 2 deletions test/loadPagesJson.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,114 @@
import { resolve } from 'node:path'
import { expect, it } from 'vitest'
import { loadPagesJson } from '../src/utils'
import { describe, expect, it } from 'vitest'
import { loadPagesJson, parsePagesJson } from '../src/utils'

it('load pages', () => {
const cwd = resolve(__dirname, 'fixtures')
const pagesJson = loadPagesJson('src/pages.json', cwd)
expect(pagesJson).toMatchSnapshot()
})

describe('parsePagesJson', () => {
it('ifdef APP-PLUS', () => {
const jsonString = `{
"pages": [
// #ifdef APP-PLUS
{
"path": "pages/index/index",
}
// #endif
]
}`
expect(parsePagesJson(jsonString, 'APP-PLUS')).toEqual({
pages: [
{
path: 'pages/index/index',
},
],
})
expect(parsePagesJson(jsonString, 'APP')).toEqual({
pages: [],
})
})
it('ifdef APP', () => {
const jsonString = `{
"pages": [
// #ifdef APP
{
"path": "pages/index/index",
}
// #endif
]
}`
expect(parsePagesJson(jsonString, 'APP-PLUS')).toEqual({
pages: [
{
path: 'pages/index/index',
},
],
})
expect(parsePagesJson(jsonString, 'APP')).toEqual({
pages: [
{
path: 'pages/index/index',
},
],
})
})
it('ifdef H5', () => {
const jsonString = `{
"pages": [
// #ifdef H5
{
"path": "pages/index/index",
}
// #endif
]
}`
expect(parsePagesJson(jsonString, 'WEB')).toEqual({
pages: [
{
path: 'pages/index/index',
},
],
})
expect(parsePagesJson(jsonString, 'APP')).toEqual({
pages: [],
})
})
it('ifndef and ||', () => {
const jsonString = `{
"pages": [
{
// #ifdef H5 || MP-WEIXIN
"path": "pages/index/index"
// #endif
// #ifndef H5 || MP-WEIXIN
"path": "pages/index/index1"
// #endif
}
]
}`
expect(parsePagesJson(jsonString, 'WEB')).toEqual({
pages: [
{
path: 'pages/index/index',
},
],
})
expect(parsePagesJson(jsonString, 'MP-WEIXIN')).toEqual({
pages: [
{
path: 'pages/index/index',
},
],
})
expect(parsePagesJson(jsonString, 'MP-ALIPAY')).toEqual({
pages: [
{
path: 'pages/index/index1',
},
],
})
})
})