日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

ssm 项目cannot resolve package_前端工程化之创建项目

發布時間:2024/7/23 HTML 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ssm 项目cannot resolve package_前端工程化之创建项目 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

在我們團隊,剛開始創建項目,是直接使用框架的 cli 進行創建項目,并修改相關配置。隨著項目的增多,沉淀了兩套模板,平臺端及移動端。后來,我們自己寫了一個簡單的 cli,并提供了 create 及 lint 命令。但由于模板的問題,一直沒有派上用場。
最近,我們正在進一步完善團隊的基礎設施。因此,期望將創建項目的功能獨立出來,并做得更加簡單易用。

實現方案

目前社區主流的創建項目主要有兩種方案。一種是集成在 cli 當中,全局安裝后進行創建項目,另外一種是使用 npm 或 yarn 提供的 create 方案,這也是我們這次選擇的方案。
使用方式如下:

$ npm init company-app [appName]
or
$ yarn create company-app [appName]

一般是執行 create 命令后,輸入項目名稱及選擇相應模板即可創建項目。在我們的團隊,是有約定項目命名 admin 結尾為平臺端項目,mobile 結尾為移動端項目。因此,可以通過判斷輸入的目錄名稱判斷是否可以直接自動選擇模板。

梳理下來的方案流程圖如下:

技術選型

在確定我們的方案后,通過閱讀社區的一些相關項目源碼,了解到在命令行及其交互方面,是有挺多的選擇的。在了解相關類庫后,可以通過 NPM Trends 可以查詢相關類庫的下載量、stars、forks、issues、updated、created、size 等數據比較。

命令行相關類庫比較:

命令行交互相關類庫比較:

在類庫選擇方面,這次我們的主要考量因素有:主流、維護情況好、體積小。因此,命令行類庫選擇了 commander、prompts。
另外,還使用 chalk 做命令行文案樣式處理、cross-spawn 做跨平臺執行命令、@zeit/ncc 來打包構建項目。
值得一提的是,@zeit/ncc 會將整個項目及相關依賴打包成一個文件。這使得我們的創建項目時,非常快速。因為只需要安裝一個包,而無需對包相關的依賴進行分析、下載、執行等。

代碼實現

  • 初始化項目,并安裝依賴。
    目錄結構如下:

  • ├── src
    │ ├── create/ # create 邏輯目錄
    │ ├── utils/ # 工具函數目錄
    │ └── index.ts # 命令入口
    ├── templates/ # 模板目錄
    ├── package.json

    └── tsconfig.json

    package.json?如下:

    {"name": "create-company-app","version": "0.0.1","description": "Create apps with one command","bin": {"create-company-app": "./dist/index.js"
    },"files": ["dist"
    ],"scripts": {"clean": "rimraf ./dist/","dev": "yarn run clean && ncc build ./src/index.ts -o dist/ -w","build": "yarn run clean && ncc build ./src/index.ts -o ./dist/ --minify --no-cache --no-source-map-register"
    },"devDependencies": {"@types/fs-extra": "^9.0.0","@types/node": "^14.0.1","@types/prompts": "^2.0.8","@types/rimraf": "^3.0.0","@types/validate-npm-package-name": "^3.0.0","@zeit/ncc": "^0.22.1","chalk": "^4.0.0","commander": "^5.1.0","cross-spawn": "^7.0.2","fs-extra": "^9.0.0","prompts": "^2.3.2","rimraf": "^3.0.2","typescript": "^3.9.2","validate-npm-package-name": "^3.0.0"
    }
    }

    tsconfig.json?如下:

    {"compilerOptions": {"target": "es2015","moduleResolution": "node","strict": true,"resolveJsonModule": true,"esModuleInterop": true,"skipLibCheck": false
    },"include": ["./src"]

    }

  • 寫一個簡單的文件夾判斷函數,及從 create-next-app 復制幾個工具函數,主要是項目名校驗及判斷 npm 包管理。

  • /utils/is-folder-exists.ts?判斷文件夾是否為空:

    import { existsSync } from 'fs';import chalk from 'chalk';export default function isFolderExists(appPath: string, appName: string) {if (existsSync(appPath)) {console.log(`The folder ${chalk.green(appName)} already exists.`);console.log('Either try using a new directory name, or remove it.');return true;}return false;}

    /utils/should-use-yarn.ts?判斷是否使用 yarn:

    import { execSync } from 'child_process';export default function shouldUseYarn(): boolean {try {const userAgent = process.env.npm_config_user_agent;if (userAgent) {return Boolean(userAgent && userAgent.startsWith('yarn'));}execSync('yarnpkg --version', { stdio: 'ignore' });return true;} catch (e) {return false;}}

    /utils/validate-pkg.ts?驗證包名是否合法:

    import validateProjectName from 'validate-npm-package-name';export function validateNpmName(name: string): { valid: boolean; problems?: string[] } {const nameValidation = validateProjectName(name);if (nameValidation.validForNewPackages) {return { valid: true };}return {valid: false,problems: [
    ...(nameValidation.errors || []),
    ...(nameValidation.warnings || []),],}}
  • 編寫命令行的入口文件?/src/index.ts?。需要注意的是,文件前面的?#!/usr/bin/env node?是必須的,具體原因可見:What exactly does “/usr/bin/env node” do at the beginning of node files?。

  • #!/usr/bin/env nodeimport chalk from 'chalk';import { Command } from 'commander';import create from './create';import packageJson from '../package.json';new Command(packageJson.name).version(packageJson.version).arguments('[project-directory]').usage(chalk.green('')).action(create).allowUnknownOption().parse(process.argv);
  • 實現創建項目核心邏輯/src/create/index.ts?創建項目流程入口文件:

  • import path from 'path';import chalk from 'chalk';import resolvePath from './resolve-path';import resolveType from './resolve-type';import copyTemplate from './copy-template';import installPkg from './install-pkg';import shouldUseYarn from '../utils/should-use-yarn';import isFolderExists from '../utils/is-folder-exists';export default async function create(inputPath: any) {const useYarn = shouldUseYarn();const originalDirectory = process.cwd();const displayedCommand = useYarn ? 'yarn' : 'npm run';const appPath = await resolvePath(inputPath);const appType = await resolveType(appPath);const appName = path.basename(appPath);const cdPath = path.join(originalDirectory, appName) === appPath ? appName : appPath;if (isFolderExists(appPath, appName)) {process.exit(1);}console.log(`Creating a new app in ${chalk.green(appPath)}.`);console.log();await copyTemplate({
    appPath,
    appType,});console.log('Installing packages. This might take a couple of minutes.');console.log();await installPkg({
    appPath,
    useYarn,});console.log(`${chalk.green('Success!')} Created ${appName} at ${appPath}`);console.log('Inside that directory, you can run several commands:');console.log();console.log(chalk.cyan(` ${displayedCommand} dev`));console.log(' Starts the development server.');console.log();console.log(chalk.cyan(` ${displayedCommand} build`));console.log(' Builds the app for production.');console.log();console.log('We suggest that you begin by typing:');console.log();console.log(chalk.cyan(' cd'), cdPath);console.log(` ${chalk.cyan(`${displayedCommand} dev`)}`);console.log();}

    /src/create/resolve-path.ts?解析項目名稱:

    import path from 'path';import chalk from 'chalk';import prompts from 'prompts';import packageJson from '../../package.json';import { validateNpmName } from '../utils/validate-pkg';const commandName = packageJson.name;export default async function resolvePath(input: string): Promise<string> {let name = input?.trim();if (!name) {const { answer } = await prompts({type: 'text',name: 'answer',message: 'What is your project named?',validate: name => {const validation = validateNpmName(path.basename(path.resolve(name)));if (validation.valid) {return true;}return 'Invalid project name: ' + validation.problems![0];},});console.log(answer);if (typeof answer === 'string') {name = answer.trim();}}if (!name) {console.log()console.log('Please specify the project directory:')console.log(` ${chalk.cyan(commandName)} ${chalk.green('')}`)console.log()console.log('For example:')console.log(` ${chalk.cyan(commandName)} ${chalk.green('app-admin')}`)console.log()console.log(`Run ${chalk.cyan(`${commandName} --help`)} to see all options.`)process.exit(1);}const projectPath = path.resolve(name);const projectName = path.basename(projectPath);const { valid, problems } = validateNpmName(projectName);if (!valid) {console.error(`Could not create a project called ${chalk.red( `"${projectName}"` )} because of npm naming restrictions:`)problems!.forEach(p => console.error(` ${chalk.red.bold('*')} ${p}`))process.exit(1)}return projectPath;}

    /src/create/resolve-type.ts?解析項目模板類型:

    import * as path from 'path';import prompts from 'prompts';const appTypeList = ['admin', 'mobile'];export default async function resolveType(input: string): Promise<string> {let appType;const projectPath = path.resolve(input);const lastStr = path.basename(projectPath).split('-').pop();if (lastStr && appTypeList.includes(lastStr)) {appType = lastStr;} else {const { answer } = await prompts({type: 'select',name: 'answer',message: 'Pick a template',choices: appTypeList.map(i => ({ title: i, value: i })),});appType = answer;}return appType;}

    /src/create/copy-template.ts?復制模板并創建項目(需要自行準備一些模板):

    import { copySync, readFileSync, writeFileSync } from 'fs-extra';import path from 'path';type Params = {appName: string;appType: string;appPath: string;};export default async function copyTemplate({ appName, appPath, appType }: Params) {const templatePath = path.join(__dirname, `../../templates/${appType}`);copySync(templatePath, appPath);const pkgPath = path.join(appPath, 'package.json');const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));pkg.name = appName;writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));}

    /src/create/install-pkg.ts?安裝項目依賴:

    import spawn from 'cross-spawn';type Params = {appPath: string;useYarn: boolean;};export default async function installPkg({ appPath, useYarn }: Params): Promise<void> {return new Promise((resolve, reject) => {process.chdir(appPath);const command = useYarn ? 'yarn' : 'npm';const args = ['install'];const child = spawn(command, args, {stdio: 'inherit',env: { ...process.env, ADBLOCK: '1', DISABLE_OPENCOLLECTIVE: '1' },});child.on('close', code => {if (code !== 0) {reject({ command: `${command} ${args.join(' ')}` });return;}resolve();})});

    }

  • 調試發包,本地可以使用?link?進行調試。

  • $ yarn run dev
    $ yarn link

    結語

    以上就是一個簡單的創建項目命令行庫的代碼實現。包括模板,構建打包后,gzip 體積不到 100kb。不算安裝依賴,創建項目非常快。
    隨著業務的發展,我們可能會增加更多功能。比如集成在 Gitlab 創建項目、在 Jenkins 上做好相關配置等。

    參考資料

    • create-next-app:?https://github.com/zeit/next.js/tree/canary/packages/create-next-app

    • create-react-native-app:?https://github.com/expo/create-react-native-app

    • create-react-app:?https://github.com/facebook/create-react-app

    • create-umi:?https://github.com/umijs/create-umi

    • commander vs yargs vs @oclif/command vs cac vs func:?https://www.npmtrends.com/commander-vs-yargs-vs-@oclif/command-vs-cac-vs-func

    • inquirer vs enquirer vs prompts:?https://www.npmtrends.com/inquirer-vs-enquirer-vs-prompts

    總結

    以上是生活随笔為你收集整理的ssm 项目cannot resolve package_前端工程化之创建项目的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。