vs2010网站开发示例,自己做视频网站怎么让加载速度变快,广告推广方案范文,网页设计网站世界杯文章目录 命令交互输出渐变标题解析命令行参数命令行交互国际化提示prompts 库实现命令行交互 生成模版创建项目输出文件夹生成 packge.json查找预设的模版文件根据路径生成模块文件render 生成模版填充 ejs 模版数据根据生成项目是 ts 还是 js 后置处理根据需要的模块生成所有… 文章目录 命令交互输出渐变标题解析命令行参数命令行交互国际化提示prompts 库实现命令行交互 生成模版创建项目输出文件夹生成 packge.json查找预设的模版文件根据路径生成模块文件render 生成模版填充 ejs 模版数据根据生成项目是 ts 还是 js 后置处理根据需要的模块生成所有对应的 README.md 文件 结尾提示 命令交互
输出渐变标题
async function init() {// process.stdout.isTTY 是否在终端运行// process.stdout.getColorDepth() 支持的颜色数量console.log(process.stdout.isTTY process.stdout.getColorDepth() 8? // banners.gradientBanner 彩色文字banners.gradientBanner: banners.defaultBanner)...
}banners
const defaultBanner Vue.js - The Progressive JavaScript Framework// generated by the following code:
//
// require(gradient-string)([
// { color: #42d392, pos: 0 },
// { color: #42d392, pos: 0.1 },
// { color: #647eff, pos: 1 }
// ])(Vue.js - The Progressive JavaScript Framework))
//
// Use the output directly here to keep the bundle small.
const gradientBanner \x1B[38;2;66;211;146mV\x1B[39m\x1B[38;2;66;211;146mu\x1B[39m\x1B[38;2;66;211;146me\x1B[39m\x1B[38;2;66;211;146m.\x1B[39m\x1B[38;2;66;211;146mj\x1B[39m\x1B[38;2;67;209;149ms\x1B[39m \x1B[38;2;68;206;152m-\x1B[39m \x1B[38;2;69;204;155mT\x1B[39m\x1B[38;2;70;201;158mh\x1B[39m\x1B[38;2;71;199;162me\x1B[39m \x1B[38;2;72;196;165mP\x1B[39m\x1B[38;2;73;194;168mr\x1B[39m\x1B[38;2;74;192;171mo\x1B[39m\x1B[38;2;75;189;174mg\x1B[39m\x1B[38;2;76;187;177mr\x1B[39m\x1B[38;2;77;184;180me\x1B[39m\x1B[38;2;78;182;183ms\x1B[39m\x1B[38;2;79;179;186ms\x1B[39m\x1B[38;2;80;177;190mi\x1B[39m\x1B[38;2;81;175;193mv\x1B[39m\x1B[38;2;82;172;196me\x1B[39m \x1B[38;2;83;170;199mJ\x1B[39m\x1B[38;2;83;167;202ma\x1B[39m\x1B[38;2;84;165;205mv\x1B[39m\x1B[38;2;85;162;208ma\x1B[39m\x1B[38;2;86;160;211mS\x1B[39m\x1B[38;2;87;158;215mc\x1B[39m\x1B[38;2;88;155;218mr\x1B[39m\x1B[38;2;89;153;221mi\x1B[39m\x1B[38;2;90;150;224mp\x1B[39m\x1B[38;2;91;148;227mt\x1B[39m \x1B[38;2;92;145;230mF\x1B[39m\x1B[38;2;93;143;233mr\x1B[39m\x1B[38;2;94;141;236ma\x1B[39m\x1B[38;2;95;138;239mm\x1B[39m\x1B[38;2;96;136;243me\x1B[39m\x1B[38;2;97;133;246mw\x1B[39m\x1B[38;2;98;131;249mo\x1B[39m\x1B[38;2;99;128;252mr\x1B[39m\x1B[38;2;100;126;255mk\x1B[39mexport { defaultBanner, gradientBanner }解析命令行参数
async function init() {const cwd process.cwd()// possible options:// --default// --typescript / --ts// --jsx// --router / --vue-router// --pinia// --with-tests / --tests (equals to --vitest --cypress)// --vitest// --cypress// --nightwatch// --playwright// --eslint// --eslint-with-prettier (only support prettier through eslint for simplicity)// --force (for force overwriting)// 解析命令行参数const argv minimist(process.argv.slice(2), {alias: {typescript: [ts], // 别名映射 typescript 还会被映射成 tswith-tests: [tests],router: [vue-router]},string: [_],// all arguments are treated as booleansboolean: true})// if any of the feature flags is set, we would skip the feature prompts// 是否命令行传入了参数传入了则跳过后续交互式选择const isFeatureFlagsUsed typeof (argv.default ??argv.ts ??argv.jsx ??argv.router ??argv.pinia ??argv.tests ??argv.vitest ??argv.cypress ??argv.nightwatch ??argv.playwright ??argv.eslint) boolean// 获取创建的文件名let targetDir argv._[0]console.log(targetDir, targetDir)const defaultProjectName !targetDir ? vue-project : targetDirconst forceOverwrite argv.force}命令行交互
国际化提示 // 根据用户时区语言拿到对应的预设国际化内容 locales文件夹下const language getLanguage()// 返回用户语言
function getLocale() {const shellLocale Intl.DateTimeFormat().resolvedOptions().locale || // Built-in ECMA-402 supportprocess.env.LC_ALL || // POSIX locale environment variablesprocess.env.LC_MESSAGES ||process.env.LANG ||// TODO: Windows support if needed, could consider https://www.npmjs.com/package/os-localeen-US // Default fallbackconst locale shellLocale.split(.)[0].replace(_, -)return locale
}// 拿到国际化文件内容
export default function getLanguage() {const locale getLocale()// Note here __dirname would not be transpiled,// so it refers to the __dirname of the file repositoryRoot/outfile.cjs// TODO: use glob import once https://github.com/evanw/esbuild/issues/3320 is fixedconst localesRoot path.resolve(__dirname, locales)const languageFilePath path.resolve(localesRoot, ${locale}.json)const doesLanguageExist fs.existsSync(languageFilePath)if (!doesLanguageExist) {console.warn(\x1B[33mThe locale langage ${locale} is not supported, fallback to en-US.\n\x1B[39m)}const lang: Language doesLanguageExist? require(languageFilePath): require(path.resolve(localesRoot, en-US.json))return lang
}国际化配置文件如下所示
prompts 库实现命令行交互
let result: {projectName?: stringshouldOverwrite?: booleanpackageName?: stringneedsTypeScript?: booleanneedsJsx?: booleanneedsRouter?: booleanneedsPinia?: booleanneedsVitest?: booleanneedsE2eTesting?: false | cypress | nightwatch | playwrightneedsEslint?: booleanneedsPrettier?: boolean} {}try {// Prompts:// - Project name:// - whether to overwrite the existing directory or not?// - enter a valid package name for package.json// - Project language: JavaScript / TypeScript// - Add JSX Support?// - Install Vue Router for SPA development?// - Install Pinia for state management?// - Add Cypress for testing?// - Add Nightwatch for testing?// - Add Playwright for end-to-end testing?// - Add ESLint for code quality?// - Add Prettier for code formatting?console.log(target, targetDir)result await prompts([{name: projectName,type: targetDir ? null : text,message: language.projectName.message,initial: defaultProjectName,onState: (state) (targetDir String(state.value).trim() || defaultProjectName)},{name: shouldOverwrite,type: () (canSkipEmptying(targetDir) || forceOverwrite ? null : toggle),message: () {const dirForPrompt targetDir .? language.shouldOverwrite.dirForPrompts.current: ${language.shouldOverwrite.dirForPrompts.target} ${targetDir}return ${dirForPrompt} ${language.shouldOverwrite.message}},initial: true,active: language.defaultToggleOptions.active,inactive: language.defaultToggleOptions.inactive},{name: overwriteChecker,type: (prev, values) {if (values.shouldOverwrite false) {throw new Error(red(✖) ${language.errors.operationCancelled})}return null}},{name: packageName, //输入package.json包名默认和项目名 targetDir 一致type: () (isValidPackageName(targetDir) ? null : text),message: language.packageName.message,initial: () toValidPackageName(targetDir), // 不合法的 targetDir 会进行转换validate: (dir) isValidPackageName(dir) || language.packageName.invalidMessage},{name: needsTypeScript,type: () (isFeatureFlagsUsed ? null : toggle),message: language.needsTypeScript.message,initial: false,active: language.defaultToggleOptions.active,inactive: language.defaultToggleOptions.inactive},{name: needsJsx,type: () (isFeatureFlagsUsed ? null : toggle),message: language.needsJsx.message,initial: false,active: language.defaultToggleOptions.active,inactive: language.defaultToggleOptions.inactive},{name: needsRouter,type: () (isFeatureFlagsUsed ? null : toggle),message: language.needsRouter.message,initial: false,active: language.defaultToggleOptions.active,inactive: language.defaultToggleOptions.inactive},{name: needsPinia,type: () (isFeatureFlagsUsed ? null : toggle),message: language.needsPinia.message,initial: false,active: language.defaultToggleOptions.active,inactive: language.defaultToggleOptions.inactive},{name: needsVitest,type: () (isFeatureFlagsUsed ? null : toggle),message: language.needsVitest.message,initial: false,active: language.defaultToggleOptions.active,inactive: language.defaultToggleOptions.inactive},{name: needsE2eTesting,type: () (isFeatureFlagsUsed ? null : select),hint: language.needsE2eTesting.hint,message: language.needsE2eTesting.message,initial: 0,choices: (prev, answers) [{title: language.needsE2eTesting.selectOptions.negative.title,value: false},{title: language.needsE2eTesting.selectOptions.cypress.title,description: answers.needsVitest? undefined: language.needsE2eTesting.selectOptions.cypress.desc,value: cypress},{title: language.needsE2eTesting.selectOptions.nightwatch.title,description: answers.needsVitest? undefined: language.needsE2eTesting.selectOptions.nightwatch.desc,value: nightwatch},{title: language.needsE2eTesting.selectOptions.playwright.title,value: playwright}]},{name: needsEslint,type: () (isFeatureFlagsUsed ? null : toggle),message: language.needsEslint.message,initial: false,active: language.defaultToggleOptions.active,inactive: language.defaultToggleOptions.inactive},{name: needsPrettier,type: (prev, values) {if (isFeatureFlagsUsed || !values.needsEslint) {return null}return toggle},message: language.needsPrettier.message,initial: false,active: language.defaultToggleOptions.active,inactive: language.defaultToggleOptions.inactive}],{onCancel: () {throw new Error(red(✖) ${language.errors.operationCancelled})}})} catch (cancelled) {console.log(cancelled.message)process.exit(1)}// initial wont take effect if the prompt type is null// so we still have to assign the default values hereconst {projectName,packageName projectName ?? defaultProjectName,shouldOverwrite argv.force,needsJsx argv.jsx,needsTypeScript argv.typescript,needsRouter argv.router,needsPinia argv.pinia,needsVitest argv.vitest || argv.tests,needsEslint argv.eslint || argv[eslint-with-prettier],needsPrettier argv[eslint-with-prettier]} resultconst { needsE2eTesting } resultconst needsCypress argv.cypress || argv.tests || needsE2eTesting cypressconst needsCypressCT needsCypress !needsVitestconst needsNightwatch argv.nightwatch || needsE2eTesting nightwatchconst needsNightwatchCT needsNightwatch !needsVitestconst needsPlaywright argv.playwright || needsE2eTesting playwright生成模版
创建项目输出文件夹 const root path.join(cwd, targetDir)// 递归删除文件夹和文件if (fs.existsSync(root) shouldOverwrite) {emptyDir(root)} else if (!fs.existsSync(root)) {fs.mkdirSync(root)}function emptyDir(dir) {if (!fs.existsSync(dir)) {return}postOrderDirectoryTraverse(dir,(dir) fs.rmdirSync(dir),(file) fs.unlinkSync(file))
}export function postOrderDirectoryTraverse(dir, dirCallback, fileCallback) {for (const filename of fs.readdirSync(dir)) {if (filename .git) {continue}const fullpath path.resolve(dir, filename)if (fs.lstatSync(fullpath).isDirectory()) {postOrderDirectoryTraverse(fullpath, dirCallback, fileCallback)dirCallback(fullpath)continue}fileCallback(fullpath)}
}
生成 packge.json console.log(\n${language.infos.scaffolding} ${root}...)const pkg { name: packageName, version: 0.0.0 }fs.writeFileSync(path.resolve(root, package.json), JSON.stringify(pkg, null, 2))查找预设的模版文件 const templateRoot path.resolve(__dirname, template)// ejs 模版提供了渲染数据的回调const callbacks []const render function render(templateName) {const templateDir path.resolve(templateRoot, templateName)renderTemplate(templateDir, root, callbacks)}预设模版文件
根据路径生成模块文件
[node_modules, package.json, extensions.json, _gitignore, data.mjs] 以外的模块直接复制
function renderTemplate(src, dest, callbacks) {const stats fs.statSync(src)// path.basename 返回当前路径的目录或文件if (stats.isDirectory()) {// skip node_moduleif (path.basename(src) node_modules) {return}// if its a directory, render its subdirectories and files recursively// dest 使用者要创建的目录地址fs.mkdirSync(dest, { recursive: true })// 递归文件夹创建文件for (const file of fs.readdirSync(src)) {renderTemplate(path.resolve(src, file), path.resolve(dest, file), callbacks)}return}const filename path.basename(src)// 在上一步已经根据 dest 创建好了 package.jsonif (filename package.json fs.existsSync(dest)) {// merge instead of overwritingconst existing JSON.parse(fs.readFileSync(dest, utf8))const newPackage JSON.parse(fs.readFileSync(src, utf8))// 合并、去重、排序(字符序) package.jsonconst pkg sortDependencies(deepMerge(existing, newPackage))fs.writeFileSync(dest, JSON.stringify(pkg, null, 2) \n)return}if (filename extensions.json fs.existsSync(dest)) {// merge instead of overwritingconst existing JSON.parse(fs.readFileSync(dest, utf8))const newExtensions JSON.parse(fs.readFileSync(src, utf8))const extensions deepMerge(existing, newExtensions)fs.writeFileSync(dest, JSON.stringify(extensions, null, 2) \n)return}if (filename.startsWith(_)) {// rename _file to .filedest path.resolve(path.dirname(dest), filename.replace(/^_/, .))}// 追加 .gitignoreif (filename _gitignore fs.existsSync(dest)) {// append to existing .gitignoreconst existing fs.readFileSync(dest, utf8)const newGitignore fs.readFileSync(src, utf8)fs.writeFileSync(dest, existing \n newGitignore)return}// data file for EJS templates// node 环境中使用 mjs 语法import 只能导入 .mjs 模块if (filename.endsWith(.data.mjs)) {// use dest path as key for the data storedest dest.replace(/\.data\.mjs$/, )// Add a callback to the array for late usage when template files are being processedcallbacks.push(async (dataStore) {const getData (await import(pathToFileURL(src).toString())).default// Though current getData are all sync, we still retain the possibility of asyncdataStore[dest] await getData({oldData: dataStore[dest] || {}})})return // skip copying the data file}// [node_modules, package.json, extensions.json, _gitignore, data.mjs] 以外的模块直接复制fs.copyFileSync(src, dest)
}render 生成模版 // Render base templaterender(base)// Add configs.if (needsJsx) {render(config/jsx)}if (needsRouter) {render(config/router)}if (needsPinia) {render(config/pinia)}if (needsVitest) {render(config/vitest)}if (needsCypress) {render(config/cypress)}if (needsCypressCT) {render(config/cypress-ct)}if (needsNightwatch) {render(config/nightwatch)}if (needsNightwatchCT) {render(config/nightwatch-ct)}if (needsPlaywright) {render(config/playwright)}if (needsTypeScript) {render(config/typescript)// Render tsconfigsrender(tsconfig/base)if (needsCypress) {render(tsconfig/cypress)}if (needsCypressCT) {render(tsconfig/cypress-ct)}if (needsPlaywright) {render(tsconfig/playwright)}if (needsVitest) {render(tsconfig/vitest)}if (needsNightwatch) {render(tsconfig/nightwatch)}if (needsNightwatchCT) {render(tsconfig/nightwatch-ct)}}// Render ESLint configif (needsEslint) {renderEslint(root, { needsTypeScript, needsCypress, needsCypressCT, needsPrettier })}// Render code template.// prettier-ignoreconst codeTemplate (needsTypeScript ? typescript- : ) (needsRouter ? router : default)render(code/${codeTemplate})// Render entry file (main.js/ts).if (needsPinia needsRouter) {render(entry/router-and-pinia)} else if (needsPinia) {render(entry/pinia)} else if (needsRouter) {render(entry/router)} else {render(entry/default)}填充 ejs 模版数据
// An external data store for callbacks to share dataconst dataStore {}// Process callbacksfor (const cb of callbacks) {await cb(dataStore)}// EJS template rendering// 从生成的 root 目录开始渲染 EJS 模版preOrderDirectoryTraverse(root,() {},(filepath) {if (filepath.endsWith(.ejs)) {const template fs.readFileSync(filepath, utf-8)const dest filepath.replace(/\.ejs$/, )const content ejs.render(template, dataStore[dest])fs.writeFileSync(dest, content)fs.unlinkSync(filepath)}})根据生成项目是 ts 还是 js 后置处理
if (needsTypeScript) {// 转化 js 文件 - ts 文件 rename删除掉原有的 jsconfig.json// 修改 index.html 中的 js 引入preOrderDirectoryTraverse(root,() {},(filepath) {if (filepath.endsWith(.js) !FILES_TO_FILTER.includes(path.basename(filepath))) {const tsFilePath filepath.replace(/\.js$/, .ts)if (fs.existsSync(tsFilePath)) {fs.unlinkSync(filepath)} else {fs.renameSync(filepath, tsFilePath)}} else if (path.basename(filepath) jsconfig.json) {fs.unlinkSync(filepath)}})// Rename entry in index.htmlconst indexHtmlPath path.resolve(root, index.html)const indexHtmlContent fs.readFileSync(indexHtmlPath, utf8)fs.writeFileSync(indexHtmlPath, indexHtmlContent.replace(src/main.js, src/main.ts))} else {// Remove all the remaining .ts files// 不需要 ts 时 删除掉目录中的 ts 文件preOrderDirectoryTraverse(root,() {},(filepath) {if (filepath.endsWith(.ts)) {fs.unlinkSync(filepath)}})}根据需要的模块生成所有对应的 README.md 文件 // 确定包管理器: pnpm yarn npmconst userAgent process.env.npm_config_user_agent ?? const packageManager /pnpm/.test(userAgent) ? pnpm : /yarn/.test(userAgent) ? yarn : npm// README generation// 根据需要的文件生成所有的 READMEfs.writeFileSync(path.resolve(root, README.md),generateReadme({projectName: result.projectName ?? result.packageName ?? defaultProjectName,packageManager,needsTypeScript,needsVitest,needsCypress,needsNightwatch,needsPlaywright,needsNightwatchCT,needsCypressCT,needsEslint}))结尾提示 // 生成项目完成提示后续辅助工作// cd xxxif (root ! cwd) {// 假设生成的路径是/Users/username/Projects/My Projectconst cdProjectName path.relative(cwd, root)// 则控制台命令会被格式化为cd My Project而不是cd My Projectconsole.log( ${bold(green(cd ${cdProjectName.includes( ) ? ${cdProjectName} : cdProjectName}))})}// pnpm|yarn|npm installconsole.log( ${bold(green(getCommand(packageManager, install)))})// prettier formatif (needsPrettier) {console.log( ${bold(green(getCommand(packageManager, format)))})}// npm devconsole.log( ${bold(green(getCommand(packageManager, dev)))})console.log()