UNPKG

lcap

Version:

CLI for Lcap Projects, including Application and UI Library.

361 lines (310 loc) 9.5 kB
const semver = require("semver"); const axios = require("axios"); const fs = require("fs-extra"); const path = require('path'); const download = require("./download.js"); const compressing = require("compressing"); const shelljs = require("shelljs"); const inquirer = require('inquirer'); const utils = require("./utils"); const configurator = require('./config.js'); const { copyFolder } = require("./fs-utils"); const { getComponentList } = require('./project'); const logger = require('./logger'); const { UI_PREFIX, PACKAGE_FILE_NAME, MAINIFEST_FILE_NAME, DEFAULT_MANIFEST, DIST_FOLDER, LCAP_MODULES, LCAP_UI, LCAP_MODULE_FILE_NAME, } = require("./constants"); const http = axios.create({ url: "" }); function getFileURL(staticURL, libInfo, filePath) { return [staticURL, UI_PREFIX, libInfo, filePath].join("/"); } function getLcapUIFilePath(filePath) { return [LCAP_MODULES, LCAP_UI, filePath].join("/"); } function getLcapLibName({ framework, type, version }) { if (framework === "react") { return '@lcap/pc-react-ui'; } if (type === "h5") { return '@lcap/mobile-ui'; } if (semver.lt(version, '1.0.0')) { return 'cloud-ui.vusion'; } if (framework === 'vue3') { return type === 'h5' ? '@lcap/vant' : '@lcap/element-plus'; } return '@lcap/pc-ui'; } function getLcapUILibInfo({ framework, type, version, pkgName }) { if (pkgName) { return `${pkgName}@${version}`; } return `${getLcapLibName({ framework, type, version })}@${version}`; } async function getManifest(staticURL, libInfo) { let response; try { response = await http.get( getFileURL(staticURL, libInfo, MAINIFEST_FILE_NAME), { responseType: "json", responseEncoding: "utf8", }, ); if (response.status !== 200 || !response.data) { throw new Error("Unfound manifest.json"); } } catch (e) { try { response = await http.get( getFileURL(staticURL, libInfo, `dist-theme/${MAINIFEST_FILE_NAME}`), { responseType: "json", responseEncoding: "utf8", }, ); if (response.status !== 200 || !response.data) { throw new Error("Unfound manifest.json"); } } catch (e) { return { ...DEFAULT_MANIFEST, }; } } return response.data; } async function downloadPackage(env, rootPath = process.cwd()) { if (!env) { const pkgInfo = fs.readJSONSync(path.resolve(rootPath, 'package.json')); if (!pkgInfo || !pkgInfo.lcap || !pkgInfo.lcap[LCAP_UI]) { return; } env = pkgInfo.lcap[LCAP_UI]; } const { platform, type, version, framework, pkgName } = env; const staticURL = await utils.getPlatformStatic(platform); const libInfo = getLcapUILibInfo({ framework, version, type, pkgName }); const tempPath = path.resolve(rootPath, '_temp'); await compressing.tgz.uncompress( await download(getFileURL(staticURL, libInfo, PACKAGE_FILE_NAME)), tempPath, ); copyFolder(path.resolve(rootPath, '_temp/package/'), path.resolve(rootPath, getLcapUIFilePath("package"))); shelljs.rm("-rf", tempPath); } const Vue2UseCode = `import * as LcapUI from 'virtual-lcap:lcap-ui'; Vue.use(LcapUI);`; const Vue3UseCode = `import { setup as setupLcapUI } from '@storybook/vue3'; import { install } from 'virtual-lcap:lcap-ui'; setupLcapUI((app) => { app.use({ install, }); }); `; async function addVueCompositionAPI(rootPath) { const pkgPath = path.resolve(rootPath, 'package.json'); const pkg = fs.readJSONSync(pkgPath); if ((pkg.dependencies && pkg.dependencies['@vue/composition-api']) || (pkg.devDependencies && pkg.devDependencies['@vue/composition-api'])) { return; } pkg.devDependencies = { '@vue/composition-api': '^1.7.2', ...pkg.devDependencies, }; try { await fs.outputFile(pkgPath, JSON.stringify(pkg, null, 2)); } catch (e) { console.log(e); } } async function resetPreviewJS(framework, rootPath) { let preivewJSPath = path.resolve(rootPath, '.storybook/preview.js'); if (!fs.existsSync(preivewJSPath)) { preivewJSPath = path.resolve(rootPath, '.storybook/preview.ts'); if (!fs.existsSync(preivewJSPath)) { return; } } const code = fs.readFileSync(preivewJSPath, 'utf-8'); if (code.indexOf('virtual-lcap:lcap-ui') !== -1) { return; } const codes = code.split('\n'); codes.unshift('import \'virtual-lcap:lcap-ui.css\';'); if (framework && framework.startsWith('vue')) { const insertIndex = codes.findIndex((str) => !str.startsWith('import')); const code = framework === 'vue2' ? Vue2UseCode : Vue3UseCode; codes.splice(insertIndex, 0, code); } fs.writeFileSync(preivewJSPath, codes.join('\n')); } async function resetTypings(rootPath, manifest) { const dtsPath = (manifest.modules || []).find((n) => n.endsWith('index.d.ts')); const typingPath = path.resolve(rootPath, 'src/typings.d.ts'); if (!fs.existsSync(typingPath)) { return; } const code = fs.readFileSync(typingPath).toString(); if (code.indexOf('virtual-lcap:lcap-ui') !== -1) { return; } let typingCode; if (dtsPath) { typingCode = [ code, 'declare module \'virtual-lcap:lcap-ui\' {', ` export * from '@lcap-ui/${dtsPath}';`, '}', '', ].join('\n'); } else { const components = getComponentList(null, rootPath); const names = []; components.forEach((c) => { names.push(c.name); if (c.children && c.children.length > 0) { c.children.forEach((child) => { names.push(child.name); }); } }); typingCode = [ code, 'declare module \'virtual-lcap:lcap-ui\' {', ...names.map((name) => ` export const ${name}: any;`), '}', '', ].join('\n'); } fs.writeFileSync(typingPath, typingCode, 'utf-8'); } async function installLcapUI({ platform, framework, version, type, pkgName }, rootPath = process.cwd()) { const lcapUIPath = path.resolve(rootPath, [LCAP_MODULES, LCAP_UI].join('/')); if (fs.existsSync(lcapUIPath)) { shelljs.rm('-rf', lcapUIPath); } if (!platform) { platform = configurator.get('platform'); } const staticURL = await utils.getPlatformStatic(platform); const libInfo = getLcapUILibInfo({ framework, version, type, pkgName }); // 获取manifest const manifest = await getManifest(staticURL, libInfo); // 下载 manifest 中的 nasl、runtime 文件到 lcap_modules const files = []; try { await Promise.all( [] .concat(manifest.nasl || []) .concat(manifest.runtime || []) .concat((manifest.theme || []).filter((str) => str.endsWith(".json"))) .concat((manifest.i18n || []).filter((str) => str.endsWith(".json"))) .concat((manifest.style || []).filter((str) => str.endsWith(".json"))) .map(async (filePath) => { files.push([DIST_FOLDER, path.basename(filePath)].join('/')); await download( getFileURL(staticURL, libInfo, filePath), path.resolve(rootPath, getLcapUIFilePath(DIST_FOLDER)), ); }), ); } catch (e) { logger.error(e); logger.error(`下载文件失败 ${JSON.stringify(files)}`) throw e; } const env = { platform, framework, version, type, pkgName }; fs.writeJSONSync( path.resolve(rootPath, getLcapUIFilePath(LCAP_MODULE_FILE_NAME)), { files, modules: (manifest.modules || []).find((s) => s.endsWith('modules.json')) || '', env: { framework, platform, version, type, pkgName, }, }, { spaces: 2 }, ); await downloadPackage(env, rootPath); await resetPreviewJS(framework, rootPath); await resetTypings(rootPath, manifest); if (pkgName === '@lcap/element-ui') { await addVueCompositionAPI(rootPath); } } async function promptInstallUI(framework) { const answer = await inquirer.prompt([ { type: 'input', name: 'platform', message: '请输入平台地址:', default: configurator.get('platform'), validate(platform) { return !!platform && platform.startsWith('http'); }, filter(val) { if (val.startsWith('https')) { return val.replace('https', 'http'); } return val; }, }, { type: 'list', name: 'type', message: '请选择端:', default: 'pc', choices: [ { value: 'pc', name: 'PC端' }, { value: 'h5', name: 'H5端' }, ], }, { type: 'input', name: 'version', message: '请输入组件库版本号\n IDE 左下角点击帮助 -> 关于IDE 可查看对应端组件库版本:', default: '1.0.0', validate(version) { return !!semver.valid(version); }, }, { type: 'list', name: 'pkgName', message: '请确认UI组件库:', when: (answers) => framework === 'vue2' && answers.type === 'pc' && !semver.lt(answers.version, '1.4.0'), choices: [ { value: '@lcap/pc-ui', name: 'CloudUI(官方默认)' }, { value: '@lcap/element-ui', name: 'ElementUI' }, ], default: (input) => { return getLcapLibName({ ...input, framework }); } } ]); if (!answer.pkgName) { answer.pkgName = getLcapLibName({ ...answer, framework }); } return answer; } module.exports = { installLcapUI, downloadPackage, promptInstallUI, };