UNPKG

@tarojs/cli

Version:
293 lines (258 loc) • 12.1 kB
import * as os from 'node:os' import * as path from 'node:path' import { babelKit, fs } from '@tarojs/helper' import Page, { ConfigModificationState } from '../create/page' import { modifyPagesOrSubPackages } from '../util/createPage' // Config updates do not use the native template generator. jest.mock('@tarojs/binding', () => ({ CompilerType: { Webpack5: 'webpack5' }, CSSType: { None: 'none' }, FrameworkType: { React: 'react' } })) function updateConfig (source: string, subPkgRootPath?: string) { const ast = babelKit.parse(source, { sourceType: 'module' })! const originalCode = babelKit.generate(ast, { compact: true }).code const callback = jest.fn() babelKit.traverse(ast, { ExportDefaultDeclaration (path) { modifyPagesOrSubPackages({ path, fullPagePath: subPkgRootPath ? `${subPkgRootPath}/new/index` : 'pages/new/index', subPkgRootPath, callback }) } }) expect(callback).toHaveBeenCalledTimes(1) return { state: callback.mock.calls[0][0], code: babelKit.generate(ast, { compact: true }).code, originalCode } } describe.each(['object', 'defineAppConfig'])('create page config: %s', (format) => { const wrap = (config: string) => `export default ${format === 'object' ? config : `defineAppConfig(${config})`}` it.each([ 'pages: ["pages/home/index"]', '"pages": ["pages/home/index"]', '...baseConfig, pages: ["pages/home/index"]', '...baseConfig, "pages": ["pages/home/index"]', 'get pages() { return [] }, "pages": ["pages/home/index"]', '[pages]: [], "pages": ["pages/home/index"]' ])('appends to explicit pages: %s', (properties) => { const result = updateConfig(wrap(`{ ${properties} }`)) expect(result.state).toBe(ConfigModificationState.Success) expect(result.code).toContain('["pages/home/index","pages/new/index"]') if (properties.includes('...baseConfig')) expect(result.code).toContain('...baseConfig') if (properties.includes('[pages]')) expect(result.code).toContain('[pages]:[]') }) it.each(['pages', '"pages"'])('does not duplicate an existing page: %s', (key) => { const result = updateConfig(wrap(`{ ${key}: ["pages/new/index"] }`)) expect(result.state).toBe(ConfigModificationState.NeedLess) expect(result.code).toBe(result.originalCode) }) it.each([ ['"pages"', 'pages'], ['pages', '"pages"'], ['pages', 'pages'], ['"pages"', '"pages"'] ])('updates the last pages property: %s followed by %s', (firstKey, lastKey) => { const result = updateConfig(wrap(`{ ${firstKey}: ["ignored"], ${lastKey}: ["pages/home/index"] }`)) expect(result.state).toBe(ConfigModificationState.Success) expect(result.code).toContain(`${firstKey}:["ignored"]`) expect(result.code).toContain(`${lastKey}:["pages/home/index","pages/new/index"]`) }) it('checks duplicates in the last pages property', () => { const result = updateConfig(wrap('{ "pages": [], pages: ["pages/new/index"] }')) expect(result.state).toBe(ConfigModificationState.NeedLess) expect(result.code).toBe(result.originalCode) }) it('leaves a nonliteral last pages property unchanged', () => { const result = updateConfig(wrap('{ "pages": [], pages: pageList }')) expect(result.state).toBe(ConfigModificationState.Fail) expect(result.code).toBe(result.originalCode) }) it.each([ '{}', '{ ...baseConfig }', '{ pages: pageList }', '{ [pages]: [] }', '{ ["pages"]: [] }', '{ pages() { return [] } }' ])('leaves unsupported pages config unchanged: %s', (config) => { const result = updateConfig(wrap(config)) expect(result.state).toBe(ConfigModificationState.Fail) expect(result.code).toBe(result.originalCode) }) it.each([ 'subPackages: [{ root: "pkg/", pages: ["home/index"] }]', '"subPackages": [{ "root": "pkg/", "pages": ["home/index"] }]', '...baseConfig, "subPackages": [{ ...subConfig, "root": "pkg/", "pages": ["home/index"] }]', 'subPackages: [{ root() {}, root: "pkg/", pages() {}, pages: ["home/index"] }]' ])('updates an existing subpackage: %s', (properties) => { const result = updateConfig(wrap(`{ ${properties} }`), 'pkg') expect(result.state).toBe(ConfigModificationState.Success) expect(result.code).toContain('["home/index","new/index"]') expect(result.code.match(/"pkg\/"/g)).toHaveLength(1) }) it('does not duplicate a page in a quoted subpackage config', () => { const result = updateConfig(wrap('{ "subPackages": [{ "root": "pkg/", "pages": ["new/index"] }] }'), 'pkg') expect(result.state).toBe(ConfigModificationState.NeedLess) expect(result.code).toBe(result.originalCode) }) it.each([ '{ "subPackages": [{ root: "ignored/", pages: [] }], subPackages: [{ root: "pkg/", pages: ["home/index"] }] }', '{ subPackages: [{ "root": "ignored/", root: "pkg/", pages: ["home/index"] }] }', '{ subPackages: [{ root: "pkg/", "pages": ["ignored"], pages: ["home/index"] }] }' ])('updates the effective subpackage properties: %s', (config) => { const result = updateConfig(wrap(config), 'pkg') expect(result.state).toBe(ConfigModificationState.Success) expect(result.code).toContain('pages:["home/index","new/index"]') expect(result.code).toContain('"ignored') expect(result.code.match(/"pkg\/"/g)).toHaveLength(1) }) it('matches the root property instead of an unrelated string value', () => { const result = updateConfig(wrap('{ subPackages: [{ name: "pkg/", root: "other/", pages: [] }, { "root": "pkg/", "pages": ["home/index"] }] }'), 'pkg') expect(result.state).toBe(ConfigModificationState.Success) expect(result.code).toContain('root:"other/",pages:[]') expect(result.code).toContain('"root":"pkg/","pages":["home/index","new/index"]') }) it('adds a missing subPackages property', () => { const result = updateConfig(wrap('{ pages: [] }'), 'pkg') expect(result.state).toBe(ConfigModificationState.Success) expect(result.code).toContain('subPackages:[{root:"pkg/",pages:["new/index"]}]') }) it.each([ 'pages() {}', 'get pages() { return [] }', 'set pages(value) {}', '[key]: []', '...baseConfig' ])('does not update pages shadowed by %s', (member) => { const result = updateConfig(wrap(`{ pages: [], ${member} }`)) expect(result.state).toBe(ConfigModificationState.Fail) expect(result.code).toBe(result.originalCode) }) it.each([ 'subPackages() {}', 'get subPackages() { return [] }', 'set subPackages(value) {}', '[key]: []', '...baseConfig' ])('does not update or add subPackages shadowed by %s', (member) => { for (const properties of [member, `subPackages: [], ${member}`]) { const result = updateConfig(wrap(`{ ${properties} }`), 'pkg') expect(result.state).toBe(ConfigModificationState.Fail) expect(result.code).toBe(result.originalCode) } }) it.each([ 'root() {}', 'get root() { return "pkg/" }', 'set root(value) {}', '[key]: "pkg/"', '...subConfig' ])('does not update or add a subpackage with root shadowed by %s', (member) => { for (const properties of [`${member}, pages: []`, `root: "pkg/", ${member}, pages: []`]) { const result = updateConfig(wrap(`{ subPackages: [{ ${properties} }] }`), 'pkg') expect(result.state).toBe(ConfigModificationState.Fail) expect(result.code).toBe(result.originalCode) } }) it.each([ 'pages() {}', 'get pages() { return [] }', 'set pages(value) {}', '[key]: []', '...subConfig' ])('does not update subpackage pages shadowed by %s', (member) => { const result = updateConfig(wrap(`{ subPackages: [{ root: "pkg/", pages: [], ${member} }] }`), 'pkg') expect(result.state).toBe(ConfigModificationState.Fail) expect(result.code).toBe(result.originalCode) }) it.each([ '{ pages() {}, pages: [] }', '{ get pages() { return [] }, pages: [] }', '{ set pages(value) {}, pages: [] }', '{ [key]: [], pages: [] }', '{ ...baseConfig, pages: [] }' ])('updates pages when an explicit array overrides earlier members: %s', (config) => { const result = updateConfig(wrap(config)) expect(result.state).toBe(ConfigModificationState.Success) expect(result.code).toContain('pages:["pages/new/index"]') }) it('does not add a subpackage when an existing root is dynamic', () => { const result = updateConfig(wrap('{ subPackages: [{ root: packageRoot, pages: [] }] }'), 'pkg') expect(result.state).toBe(ConfigModificationState.Fail) expect(result.code).toBe(result.originalCode) }) const unresolvedSubPackages = [ '{ root: packageRoot, pages: [] }', '{ get root() { return "pkg/" }, pages: [] }', '{ [key]: "pkg/", pages: [] }', '{ ...subConfig, pages: [] }', '...otherPackages' ] it.each(unresolvedSubPackages)('updates an explicit subpackage alongside an unresolved entry: %s', (entry) => { const target = '{ root: "pkg/", pages: ["home/index"] }' for (const entries of [`${entry}, ${target}`, `${target}, ${entry}`]) { const result = updateConfig(wrap(`{ subPackages: [${entries}] }`), 'pkg') expect(result.state).toBe(ConfigModificationState.Success) expect(result.code).toContain('root:"pkg/",pages:["home/index","new/index"]') expect(result.code.replace(',"new/index"', '')).toBe(result.originalCode) } }) it.each(unresolvedSubPackages)('does not add a subpackage when no root matches and an entry is unresolved: %s', (entry) => { const result = updateConfig(wrap(`{ subPackages: [${entry}, { root: "other/", pages: [] }] }`), 'pkg') expect(result.state).toBe(ConfigModificationState.Fail) expect(result.code).toBe(result.originalCode) }) it('adds a subpackage when all existing roots are known and different', () => { const result = updateConfig(wrap('{ subPackages: [{ root: "other/", pages: [] }] }'), 'pkg') expect(result.state).toBe(ConfigModificationState.Success) expect(result.code).toContain('subPackages:[{root:"other/",pages:[]},{root:"pkg/",pages:["new/index"]}]') }) it.each([ '{ "subPackages": subPackages }', '{ "subPackages": [{ "root": "pkg/", "pages": pageList }] }' ])('leaves nonliteral subpackage configs unchanged: %s', (config) => { const result = updateConfig(wrap(config), 'pkg') expect(result.state).toBe(ConfigModificationState.Fail) expect(result.code).toBe(result.originalCode) }) }) it.each(['defineAppConfig()', 'defineAppConfig(config)', 'defineAppConfig(null)'])( 'leaves a non-object config unchanged: %s', (config) => { for (const subPkgRootPath of [undefined, 'pkg']) { const result = updateConfig(`export default ${config}`, subPkgRootPath) expect(result.state).toBe(ConfigModificationState.Fail) expect(result.code).toBe(result.originalCode) } } ) describe('create page config file updates', () => { let projectDir: string beforeEach(() => { projectDir = fs.mkdtempSync(path.join(os.tmpdir(), 'taro-create-page-')) jest.spyOn(console, 'log').mockImplementation(() => {}) }) afterEach(() => { fs.removeSync(projectDir) jest.restoreAllMocks() }) it.each(['js', 'ts'])('writes the new page to app.config.%s', (extension) => { const configPath = path.join(projectDir, 'src', `app.config.${extension}`) const baseConfig = extension === 'ts' ? 'const baseConfig: { window?: object } = {}' : 'const baseConfig = {}' fs.outputFileSync(configPath, `${baseConfig}\nexport default defineAppConfig({ ...baseConfig, "pages": ["pages/home/index"] })`) const page = Object.create(Page.prototype) page.conf = { projectDir, typescript: extension === 'ts' } page.pageEntryPath = '/src/pages/new/index' page.updateAppConfig() const updated = fs.readFileSync(configPath, 'utf8') expect(updated).toContain('pages/home/index') expect(updated).toContain('pages/new/index') expect(updated).toContain('...baseConfig') page.updateAppConfig() expect(fs.readFileSync(configPath, 'utf8')).toBe(updated) }) })