feat(cli): start new cli

This commit is contained in:
LongYinan 2020-07-26 23:53:09 +08:00
parent 8df107f380
commit 2073f23a91
No known key found for this signature in database
GPG key ID: C3666B7FC82ADAD7
11 changed files with 1742 additions and 123 deletions

14
.eslintignore Normal file
View file

@ -0,0 +1,14 @@
node_modules
dist
lib
esm
next
coverage
output
static
temp
.nyc_output
*.d.ts
__mock__
target
scripts

214
.eslintrc.yml Normal file
View file

@ -0,0 +1,214 @@
parser: '@typescript-eslint/parser'
parserOptions:
ecmaFeatures:
jsx: true
ecmaVersion: 2020
sourceType: module
project: ./tsconfig.json
env:
browser: true
es6: true
node: true
plugins:
- import
extends:
- eslint:recommended
- plugin:prettier/recommended
rules:
# 0 = off, 1 = warn, 2 = error
'space-before-function-paren': 0
'no-useless-constructor': 0
'no-undef': 2
'no-console': [2, { allow: ['error', 'warn', 'info', 'assert'] }]
'comma-dangle': ['error', 'only-multiline']
'no-unused-vars': 0
'no-var': 2
'one-var-declaration-per-line': 2
'prefer-const': 2
'no-const-assign': 2
'no-duplicate-imports': 2
'no-use-before-define': [2, { 'functions': false, 'classes': false }]
'eqeqeq': [2, 'always', { 'null': 'ignore' }]
'no-case-declarations': 0
'no-dupe-class-members': 0
'import/first': 2
'import/newline-after-import': 2
'import/order':
[
2,
{
'newlines-between': 'always',
'alphabetize': { 'order': 'asc', 'caseInsensitive': true },
'pathGroups':
[
{
'pattern': '@slardar/**',
'group': 'internal',
'position': 'before',
},
{
'pattern': '@perfkit/**',
'group': 'internal',
'position': 'before',
},
{
'pattern': '@maiev/**',
'group': 'internal',
'position': 'before',
},
],
},
]
'@typescript-eslint/adjacent-overload-signatures': 2
'@typescript-eslint/await-thenable': 2
'@typescript-eslint/consistent-type-assertions': 2
'@typescript-eslint/ban-types':
[
'error',
{
'types':
{
'String': { 'message': 'Use string instead', 'fixWith': 'string' },
'Number': { 'message': 'Use number instead', 'fixWith': 'number' },
'Boolean':
{ 'message': 'Use boolean instead', 'fixWith': 'boolean' },
'Function': { 'message': 'Use explicit type instead' },
},
},
]
'@typescript-eslint/explicit-member-accessibility':
[
'error',
{
accessibility: 'explicit',
overrides:
{
accessors: 'no-public',
constructors: 'no-public',
methods: 'no-public',
properties: 'no-public',
parameterProperties: 'explicit',
},
},
]
'@typescript-eslint/method-signature-style': 2
'@typescript-eslint/no-floating-promises': 2
'@typescript-eslint/no-implied-eval': 2
'@typescript-eslint/no-for-in-array': 2
'@typescript-eslint/no-inferrable-types': 2
'@typescript-eslint/no-invalid-void-type': 2
'@typescript-eslint/no-misused-new': 2
'@typescript-eslint/no-misused-promises': 2
'@typescript-eslint/no-namespace': 2
'@typescript-eslint/no-non-null-asserted-optional-chain': 2
'@typescript-eslint/no-throw-literal': 2
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 2
'@typescript-eslint/prefer-for-of': 2
'@typescript-eslint/prefer-nullish-coalescing': 2
'@typescript-eslint/switch-exhaustiveness-check': 2
'@typescript-eslint/prefer-optional-chain': 2
'@typescript-eslint/prefer-readonly': 2
'@typescript-eslint/prefer-string-starts-ends-with': 0
'@typescript-eslint/no-array-constructor': 2
'@typescript-eslint/require-await': 2
'@typescript-eslint/return-await': 2
'@typescript-eslint/ban-ts-comment':
[
2,
{
'ts-expect-error': false,
'ts-ignore': true,
'ts-nocheck': true,
'ts-check': false,
},
]
'@typescript-eslint/naming-convention':
[
2,
{
selector: 'memberLike',
format: ['camelCase', 'PascalCase'],
modifiers: ['private'],
leadingUnderscore: 'forbid',
},
]
'@typescript-eslint/no-unused-vars':
[
2,
{
varsIgnorePattern: '^_',
argsIgnorePattern: '^_',
ignoreRestSiblings: true,
},
]
'@typescript-eslint/member-ordering':
[
2,
{
default:
[
'public-static-field',
'protected-static-field',
'private-static-field',
'public-static-method',
'protected-static-method',
'private-static-method',
'public-instance-field',
'protected-instance-field',
'private-instance-field',
'public-constructor',
'protected-constructor',
'private-constructor',
'public-instance-method',
'protected-instance-method',
'private-instance-method',
],
},
]
overrides:
- files:
- ./**/*.js
plugins:
- '@typescript-eslint'
parserOptions:
project: ./tsconfig.json
rules:
'@typescript-eslint/prefer-nullish-coalescing': 0
'@typescript-eslint/prefer-optional-chain': 0
'@typescript-eslint/no-non-null-asserted-optional-chain': 0

1
.gitignore vendored
View file

@ -154,3 +154,4 @@ Temporary Items
.apdisk
# End of https://www.gitignore.io/api/macos
scripts

View file

@ -1,5 +1,6 @@
target
build
sys
napi
napi-derive
napi-derive-example
@ -7,3 +8,10 @@ test_module
.yarnrc
Cargo.lock
rustfmt.toml
.github
.dockerignore
.eslintignore
.eslintrc.yml
Cargo.toml
Dockerfile.alpine
yarn.lock

View file

@ -31,6 +31,8 @@
},
"homepage": "https://github.com/napi-rs/napi-rs#readme",
"dependencies": {
"clipanion": "^2.4.2",
"inquirer": "^7.3.3",
"minimist": "^1.2.5",
"toml": "^3.0.0"
},
@ -57,11 +59,26 @@
}
},
"devDependencies": {
"@types/node": "^14.0.27",
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@types/inquirer": "^7.3.0",
"@types/node": "^14.0.26",
"@typescript-eslint/eslint-plugin": "^3.7.0",
"@typescript-eslint/parser": "^3.7.0",
"ava": "^3.11.0",
"eslint": "^7.5.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^3.1.4",
"husky": "^4.2.5",
"lint-staged": "^10.2.11",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.5"
"nyc": "^15.1.0",
"prettier": "^2.0.5",
"source-map-support": "^0.5.19",
"ts-node": "^8.10.2",
"typescript": "^3.9.7"
},
"optionalDependencies": {
"tslib": "^2.0.0"
}
}

View file

@ -1,90 +0,0 @@
#!/usr/bin/env node
const parseArgs = require('minimist')
const path = require('path')
const os = require('os')
const toml = require('toml')
const fs = require('fs')
let tomlContentString
let tomlContent
let moduleName
try {
tomlContentString = fs.readFileSync(
path.join(process.cwd(), 'Cargo.toml'),
'utf-8',
)
} catch {
throw new TypeError('Can not find Cargo.toml in process.cwd')
}
try {
tomlContent = toml.parse(tomlContentString)
} catch {
throw new TypeError('Can not parse the Cargo.toml')
}
if (tomlContent.package && tomlContent.package.name) {
moduleName = tomlContent.package.name.replace(/-/g, '_')
} else {
throw new TypeError('No package.name field in Cargo.toml')
}
const argv = parseArgs(process.argv.slice(2), {
boolean: ['release', 'platform', 'musl'],
})
const platform = os.platform()
let libExt
let dylibName = moduleName
// Platform based massaging for build commands
switch (platform) {
case 'darwin':
libExt = '.dylib'
dylibName = `lib${moduleName}`
break
case 'win32':
libExt = '.dll'
break
case 'linux':
dylibName = `lib${moduleName}`
libExt = '.so'
break
default:
console.error(
'Operating system not currently supported or recognized by the build script',
)
process.exit(1)
}
const targetDir = argv.release ? 'release' : 'debug'
const platformName = argv.musl ? '.musl' : argv.platform ? `.${platform}` : ''
let distModulePath =
argv._[0] ||
path.join('target', targetDir, `${moduleName}${platformName}.node`)
const parsedDist = path.parse(distModulePath)
if (!parsedDist.name || parsedDist.name === '.') {
distModulePath = moduleName
}
if (!parsedDist.ext) {
distModulePath = `${distModulePath}${platformName}.node`
}
const pos = __dirname.indexOf('node_modules')
const dylibContent = fs.readFileSync(
path.join(
__dirname.substring(0, pos),
'target',
targetDir,
`${dylibName}${libExt}`,
),
)
fs.writeFileSync(distModulePath, dylibContent)

117
src/build.ts Normal file
View file

@ -0,0 +1,117 @@
import { readFile, writeFile } from 'fs'
import os from 'os'
import { join, parse } from 'path'
import { promisify } from 'util'
import { Command } from 'clipanion'
import toml from 'toml'
const readFileAsync = promisify(readFile)
const writeFileAsync = promisify(writeFile)
export class BuildCommand extends Command {
static usage = Command.Usage({
description: 'Copy native module into specified dir',
})
@Command.String(`--name`)
name!: string
@Command.String(`--platform`)
appendPlatformToFilename!: string
@Command.Boolean(`--release`)
isRelease = false
@Command.Boolean('--musl')
isMusl = false
@Command.String()
target?: string
@Command.Path('build')
async execute() {
let tomlContentString: string
let tomlContent: any
let moduleName: string
try {
tomlContentString = await readFileAsync(
join(process.cwd(), 'Cargo.toml'),
'utf-8',
)
} catch {
throw new TypeError(`Could not find Cargo.toml in ${process.cwd()}`)
}
try {
tomlContent = toml.parse(tomlContentString)
} catch {
throw new TypeError('Could not parse the Cargo.toml')
}
if (tomlContent.package ?? tomlContent.package.name) {
moduleName = tomlContent.package.name.replace(/-/g, '_')
} else {
throw new TypeError('No package.name field in Cargo.toml')
}
const platform = os.platform()
let libExt
let dylibName = moduleName
// Platform based massaging for build commands
switch (platform) {
case 'darwin':
libExt = '.dylib'
dylibName = `lib${moduleName}`
break
case 'win32':
libExt = '.dll'
break
case 'linux':
dylibName = `lib${moduleName}`
libExt = '.so'
break
default:
console.error(
'Operating system not currently supported or recognized by the build script',
)
process.exit(1)
}
const targetDir = this.isRelease ? 'release' : 'debug'
const platformName = this.isMusl
? '.musl'
: this.appendPlatformToFilename
? `.${platform}`
: ''
let distModulePath =
this.target ??
join('target', targetDir, `${moduleName}${platformName}.node`)
const parsedDist = parse(distModulePath)
if (!parsedDist.name || parsedDist.name === '.') {
distModulePath = moduleName
}
if (!parsedDist.ext) {
distModulePath = `${distModulePath}${platformName}.node`
}
const pos = __dirname.indexOf('node_modules')
const dylibContent = await readFileAsync(
join(
__dirname.substring(0, pos),
'target',
targetDir,
`${dylibName}${libExt}`,
),
)
await writeFileAsync(distModulePath, dylibContent)
}
}

19
src/index.ts Normal file
View file

@ -0,0 +1,19 @@
import { Cli } from 'clipanion'
import { BuildCommand } from './build'
const cli = new Cli({
binaryName: 'bin',
binaryVersion: require('../package.json').version,
})
cli.register(BuildCommand)
cli
.run(process.argv.slice(2), {
...Cli.defaultContext,
})
.catch((e) => {
console.error(e)
process.exit(1)
})

View file

@ -2,8 +2,8 @@
"name": "test-module",
"version": "1.0.0",
"scripts": {
"build": "cargo build && node ../scripts/napi.js ./index",
"build-release": "cargo build --release && node ../scripts/napi.js --release ./index",
"build": "cargo build && node ../scripts/index.js build ./index",
"build-release": "cargo build --release && node ../scripts/napi.js build --release ./index",
"test": "node ./index.js"
}
}

44
tsconfig.json Normal file
View file

@ -0,0 +1,44 @@
{
"compilerOptions": {
"jsx": "react",
"allowSyntheticDefaultImports": true,
"declaration": true,
"downlevelIteration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"module": "CommonJS",
"moduleResolution": "node",
"newLine": "LF",
"noEmitHelpers": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"strict": true,
"skipLibCheck": true,
"suppressImplicitAnyIndexErrors": true,
"suppressExcessPropertyErrors": true,
"forceConsistentCasingInFileNames": true,
"preserveSymlinks": true,
"target": "ES2015",
"sourceMap": true,
"esModuleInterop": true,
"stripInternal": true,
"resolveJsonModule": true,
"importsNotUsedAsValues": "remove",
"outDir": "./scripts",
"lib": [
"dom",
"DOM.Iterable",
"ES5",
"ES2015",
"ES2016",
"ES2017",
"ES2018",
"ES2019",
"ES2020",
"esnext"
]
},
"include": ["./src"],
"exclude": ["node_modules"]
}

1333
yarn.lock

File diff suppressed because it is too large Load diff