记录使用typescript配合webpack打包一个javascript library的配置过程.
目标是构建一个可以同时支持CommonJs, esm, amd这个几个js模块系统的javascript库, 然后还有一个单独打包出一个css的样式文件的需求.
为此以构建一个名为loaf的javascript库为例; 首先新建项目文件目录loaf, 并进入此文件目录执行npm init命令, 然后按照控制台的提示输入对应的信息, 完成后就会在loaf目录下得到一个package.json文件

然后使用npm i命令安装所需的依赖
npm i -D webpack webpack-cli typescript babel-loader @babel/core @babel/preset-env @babel/preset-typescript ts-node @types/node @types/webpack mini-css-extract-plugin css-minimizer-webpack-plugin less less-loader terser-webpack-plugin
webpack webpack-cli: webpack打包工具和webpack命令行接口typescript: 用于支持typescript语言babel-loader @babel/core @babel/preset-env @babel/preset-typescript: babel相关的东西, 主要是需要babel-loader将编写的typescript代码转译成es5或es6已获得更好的浏览器兼容性ts-node @types/node @types/webpack: 安装这几个包是为了能用typescript编写webpack配置文件(webpack.config.ts)mini-css-extract-plugin less less-loader: 编译提取less文件到单独的css文件的相关依赖css-minimizer-webpack-plugin terser-webpack-plugin: 用于最小化js和css文件尺寸的webpack插件通常使用index.ts作为入口, 并将其放到src目录下, 由于有输出样式文件的需求, 所以还要新建styles/index.less
mkdir src && touch src/index.ts
mkdir src/styles && touch src/styles/index.less
新建tsconfig.json文件
touch tsconfig.json
填入以下配置(部分选项配有注释):
{
"compilerOptions": {
"outDir": "dist/lib",
"sourceMap": false,
"noImplicitAny": true,
"module": "commonjs",
// 开启这个选项, 可以让你直接通过`import`的方式来引用commonjs模块
// 这样你的代码库中就可以统一的使用import导入依赖了, 而不需要另外再使用require导入commonjs模块
"esModuleInterop": true,
// 是否允许合成默认导入
// 开启后, 依赖的模块如果没有导出默认的模块
// 那么typescript会帮你给该模块自动合成一个默认导出让你可以通过默认导入的方式引用该模块
"allowSyntheticDefaultImports": true,
// 是否生成`.d.ts`的类型声明文件
"declaration": true,
// 输出的目标js版本, 这里用es6, 然后配和babel进行转译才以获得良好的浏览器兼容
"target": "es6",
"allowJs": true,
"moduleResolution": "node",
"lib": ["es2015", "dom"],
"declarationMap": true,
// 启用严格的null检查
"strictNullChecks": true,
// 启用严格的属性初始化检查
// 启用后类属性必须显示标注为可空或赋一个非空的初始值
"strictPropertyInitialization": true
},
"exclude": ["node_modules"],
"include": ["src/**/*"]
}
创建webpack.config.ts
touch webpack.config.ts
import path from "path";
import { Configuration, Entry } from "webpack";
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import CssMinimizer from 'css-minimizer-webpack-plugin';
import TerserPlugin from 'terser-webpack-plugin'
const isProd = process.env.NODE_ENV === 'production';
/**
* 这里用到了webpack的[Multiple file types per entry](https://webpack.js.org/guides/entry-advanced/)特性
* 注意`.less`入口文件必须放在`.ts`文件前 */
const entryFiles: string[] = ['./src/styles/index.less', './src/index.ts'];
const entry: Entry = {
index: entryFiles,
'index.min': entryFiles,
};
const config: Configuration = {
entry,
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({ test: /.min.js$/ }),
new CssMinimizer({
test: /.min.css$/,
}),
],
},
module: {
rules: [
{
test: /.ts$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['@babel/env', '@babel/typescript'],
},
},
{
test: /.less$/,
use: [
isProd ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader',
},
'postcss-loader',
'less-loader',
],
},
],
},
output: {
path: path.resolve(__dirname, 'dist/umd'),
library: {
type: 'umd',
name: {
amd: 'loaf',
commonjs: 'loaf',
root: 'loaf',
},
},
},
resolve: {
extensions: ['.ts', '.less'],
},
devtool: 'source-map',
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
}),
],
};
export default config;
...
const isProd = process.env.NODE_ENV === 'production';
/**
* 这里用到了webpack的[Multiple file types per entry](https://webpack.js.org/guides/entry-advanced/)特性
* 注意`.less`入口文件必须放在`.ts`文件前 */
const entryFiles: string[] = ['./src/styles/index.less', './src/index.ts'];
const entry: Entry = {
index: entryFiles,
'index.min': entryFiles,
};
const config: Configuration = {
entry,
...
}
...在上面的webpack.config.json中,我们配置了两个入口分别是index和index.min,不难看出,多出的一个index.min入口是为了经过压缩后js和css文件,在生产环境使用一般都会使用.min.js结尾的文件以减少网络传输时的尺寸; 实现这个还需要结合optimization相关配置, 如下:
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({ test: /.min.js$/ }),
new CssMinimizer({
test: /.min.css$/,
}),
],
},
另外,index和index.min的值都是相同的entryFiles对象,这个对象是一个字符串数组,里面放的就是我们的入口文件相对路径,这里一定要注意把./src/styles/index.less置于./src/index.ts之前。
配置完入口后, 就需要为typescript和less代码配置各自的loader
module: {
rules: [
{
test: /.ts$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['@babel/env', '@babel/typescript'],
},
},
{
test: /.less$/,
use: [
isProd ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader',
},
'postcss-loader',
'less-loader',
],
},
],
},
mini-css-extract-plugin less less-loader: 编译提取less文件到单独的css文件的相关依赖babel-loader; 为.less结尾的文件配置一串loader, 使用了use, use中的loader的执行顺序是从后往前的, 上面less的配置就是告诉webpack遇到less文件时, 一次用less-loader->postcss-loader->css-loader->生产环境用 MiniCssExtractPlugin.loader() 否则用 style-loader;MiniCssExtractPlugin.loader使用前要先在plugins进行初始化
...
const config = {
...
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
}),
],
...
}
......
const config = {
...
output: {
path: path.resolve(__dirname, 'dist/umd'),
library: {
type: 'umd',
name: {
amd: 'loaf',
commonjs: 'loaf',
root: 'loaf',
},
},
},
...
}
...
这里配置webpack以umd的方式输出到相对目录dist/umd目录中, umd是Universal Module Definition(通用模块定义)的缩写, umd格式输出library允许用户通过commonjs, AMD, <script src="...">的方式对library进行引用config.library.name可以为不同的模块系统配置不同的导出模块名供客户端来进行引用; 由于这里的导出模块名都是loaf, 所以也可以直接config.library.name设置成loaf.
现在回到最开始通过npm init生成的package.json文件, 在修改其内容如下
{
"name": "loaf",
"version": "1.0.0",
"description": "A demo shows how to create & build a javascript library with webpack & typescript",
"main": "index.js",
"scripts": {
"build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production",
"test": "npm run test"
},
"keywords": [
"demo"
],
"author": "laggage",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.18.6",
"@babel/preset-env": "^7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@types/node": "^18.0.0",
"@types/webpack": "^5.28.0",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^4.0.0",
"less": "^4.1.3",
"less-loader": "^11.0.0",
"mini-css-extract-plugin": "^2.6.1",
"postcss-loader": "^7.0.0",
"style-loader": "^3.3.1",
"terser-webpack-plugin": "^5.3.3",
"ts-node": "^10.8.2",
"typescript": "^4.7.4",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0"
}
}新增了一个脚本命令 "build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production", 然后命令行到项目目录下执行npm run build:umd, 不出意外应该就构建成功了, 此时生成的dist目录结构如下
dist
└── umd
├── index.css
├── index.js
├── index.js.map
├── index.min.css
├── index.min.js
└── index.min.js.map
1 directory, 6 files
新建demo.html进行测试
mkdir demo && touch demo/demo.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="../dist/umd/index.js"></script>
<script type="text/javascript">
console.log(loaf, '\n', loaf.Foo)
</script>
</body>
</html>
用浏览器打开demo.html, 然后F12打开控制台, 可以看到如下输出则说明初步达成了目标:
Module {__esModule: true, Symbol(Symbol.toStringTag): 'Module'}
demo.html:13 ƒ Foo() {
var _bar = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Bar();
src_classCallCheck(this, Foo);
this._bar = _bar;
}
完成上面的步骤后, 我们已经到了一个umd模块的输出, 相关文件都在dist/umd目录下; 其中包含可供CommonJs ESM AMD模块系统和script标签使用的umd格式的javascript文件和一个单独的css样式文件.
Tree shaking is a term commonly used in the JavaScript context for dead-code elimination. It relies on the static structure of ES2015 module syntax, i.e. import and export. The name and concept have been popularized by the ES2015 module bundler rollup.
此库的使用者也使用了类似webpack之类的支持Tree Shaking
的模块打包工具,需要让使用者的打包工具能对这个js库loaf进行死代码优化Tree Shaking
从webpack文档中看出, tree-shaking依赖于ES2015(ES2015 module syntax, ES2015=ES6)的模块系统, tree-shaking可以对打包体积有不错优化, 所以为了支持使用者进行tree-shaking, 输出esm模块(esm模块就是指 ES2015 module syntax)是很有必要的.
tsc -p tsconfig.json --declarationDir ./dist/typings -m es6 --outDir dist/lib-esm
上面的命令使用typescript编译器命令行接口tsc输出了ES6模块格式的javascript文件到dist/lib-esm目录下
将这个目录加入到package.json的scripts配置中:
{
"name": "loaf",
"version": "1.0.0",
"description": "A demo shows how to create & build a javascript library with webpack & typescript",
"main": "index.js",
"scripts": {
"build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production",
"build:lib-esm": "tsc -p tsconfig.json --declarationDir ./dist/typings -m es6 --outDir dist/lib-esm",
"test": "npm run test"
},
"keywords": [
"demo"
],
"author": "laggage",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.18.6",
"@babel/preset-env": "^7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@types/node": "^18.0.0",
"@types/webpack": "^5.28.0",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^4.0.0",
"less": "^4.1.3",
"less-loader": "^11.0.0",
"mini-css-extract-plugin": "^2.6.1",
"postcss-loader": "^7.0.0",
"style-loader": "^3.3.1",
"terser-webpack-plugin": "^5.3.3",
"ts-node": "^10.8.2",
"typescript": "^4.7.4",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0"
}
}然后运行: npm run build:lib-esm, 此时dist目录结构如下:
dist
├── lib-esm
│ ├── bar.js
│ └── index.js
├── typings
│ ├── bar.d.ts
│ ├── bar.d.ts.map
│ ├── index.d.ts
│ └── index.d.ts.map
└── umd
├── index.css
├── index.js
├── index.js.map
├── index.min.css
├── index.min.js
└── index.min.js.map
3 directories, 12 files
多出了两个子目录分别为lib-esm和typings, 分别放着es6模块格式的javascript输出文件和typescript类型声明文件.
到目前为止, package.json的scripts配置中, 已经有了build:umd和build:lib-esm用于构建umd格式的输出和esm格式的输出, 现在我们再向添加一个build用来组合build:umd和build:lib-esm并进行最终的构建, 再次之前先安装一个依赖shx, 用于跨平台执行一些shell脚本: npm i -D shx;
更新package.json文件:
{
"name": "loaf",
"version": "1.0.0",
"description": "A demo shows how to create & build a javascript library with webpack & typescript",
"main": "index.js",
"scripts": {
"build": "shx rm -rf dist/** && npm run build:umd && npm run build:lib-esm",
"build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production",
"build:lib-esm": "tsc -p tsconfig.json --declarationDir ./dist/typings -m es6 --outDir dist/lib-esm",
"test": "npm run test"
},
"keywords": [
"demo"
],
"author": "laggage",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.18.6",
"@babel/preset-env": "^7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@types/node": "^18.0.0",
"@types/webpack": "^5.28.0",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^4.0.0",
"less": "^4.1.3",
"less-loader": "^11.0.0",
"mini-css-extract-plugin": "^2.6.1",
"postcss-loader": "^7.0.0",
"shx": "^0.3.4",
"style-loader": "^3.3.1",
"terser-webpack-plugin": "^5.3.3",
"ts-node": "^10.8.2",
"typescript": "^4.7.4",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0"
}
}package.json文件生成typescript声明文件所在的路径(可以参考typescript官网:Including declarations in your npm package):
{
"name": "loaf",
"version": "1.0.0",
"description": "A demo shows how to create & build a javascript library with webpack & typescript",
"main": "index.js",
"typings": "./typings",
"scripts": {
"build": "shx rm -rf dist/** && npm run build:umd && npm run build:lib-esm",
"build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production",
"build:lib-esm": "tsc -p tsconfig.json --declarationDir ./dist/typings -m es6 --outDir dist/lib-esm",
"test": "npm run test"
},
"keywords": [
"demo"
],
"author": "laggage",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.18.6",
"@babel/preset-env": "^7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@types/node": "^18.0.0",
"@types/webpack": "^5.28.0",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^4.0.0",
"less": "^4.1.3",
"less-loader": "^11.0.0",
"mini-css-extract-plugin": "^2.6.1",
"postcss-loader": "^7.0.0",
"shx": "^0.3.4",
"style-loader": "^3.3.1",
"terser-webpack-plugin": "^5.3.3",
"ts-node": "^10.8.2",
"typescript": "^4.7.4",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0"
}
}exports配置声明模块导出路径package.json中的exports字段用于告诉使用者引用此库时从哪里寻找对应的模块文件. 比如使用者可能通过esm模块引用此库:
import { Foo } from 'loaf';
const foo = new Foo();
此时如果我们的package.json中没有指定exports字段, 那么模块系统会去寻找node_modules/index.js, 结果肯定是找不到的, 因为我们真正的esm格式的输出文件应该是在node_modules/loaf/lib-esm中的
于是我们可以这样来配置exports:
{
"name": "loaf",
"version": "1.0.0",
"description": "A demo shows how to create & build a javascript library with webpack & typescript",
"main": "index.js",
"typings": "./typings",
"exports": {
"./*": "./lib-esm/*",
"./umd/*": "./umd"
},
"scripts": {
"build": "shx rm -rf dist/** && npm run build:umd && npm run build:lib-esm",
"build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production",
"build:lib-esm": "tsc -p tsconfig.json --declarationDir ./dist/typings -m es6 --outDir dist/lib-esm",
"test": "npm run test"
},
"keywords": [
"demo"
],
"author": "laggage",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.18.6",
"@babel/preset-env": "^7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@types/node": "^18.0.0",
"@types/webpack": "^5.28.0",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^4.0.0",
"less": "^4.1.3",
"less-loader": "^11.0.0",
"mini-css-extract-plugin": "^2.6.1",
"postcss-loader": "^7.0.0",
"shx": "^0.3.4",
"style-loader": "^3.3.1",
"terser-webpack-plugin": "^5.3.3",
"ts-node": "^10.8.2",
"typescript": "^4.7.4",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0"
}
}.d.ts在上面的用tsc输出esm和类型声明文件这一段中, 我们通过tsc命令输出了typescript了类型声明文件到dist/types目录下, 这个目录下有两个.d.ts文件, 分别是bar.d.ts和foo.d.ts, 通常是希望这些声明文件都在一个文件index.d.ts中的, 如果他们分散开了, 以本库为例, 如果我要使用本库中的Bar类, 那么我可能需要这样来导入:
import { Bar } from 'loaf/typings/bar';
我不觉得的这种导入方式是好的做法, 理想的导入方式应该像下面这样:
import { Bar } from 'loaf';
所以接下来, 还要引入微软提供的api-extractor
安装依赖:
npm install -D @microsoft/api-extractor
再全局安装下:
npm install -g @microsoft/api-extractor
生成api-extractor.json
api-extractor init
稍微修改下api-extractor.json
<
api-extractor.json
/**
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
*/
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
/**
* Optionally specifies another JSON config file that this file extends from. This provides a way for
* standard settings to be shared across multiple projects.
*
* If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains
* the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be
* resolved using NodeJS require().
*
* SUPPORTED TOKENS: none
* DEFAULT VALUE: ""
*/
// "extends": "./shared/api-extractor-base.json"
// "extends": "my-package/include/api-extractor-base.json"
/**
* Determines the "<projectFolder>" token that can be used with other config file settings. The project folder
* typically contains the tsconfig.json and package.json config files, but the path is user-defined.
*
* The path is resolved relative to the folder of the config file that contains the setting.
*
* The default value for "projectFolder" is the token "<lookup>", which means the folder is determined by traversing
* parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder
* that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error
* will be reported.
*
* SUPPORTED TOKENS: <lookup>
* DEFAULT VALUE: "<lookup>"
*/
// "projectFolder": "..",
/**
* (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor
* analyzes the symbols exported by this module.
*
* The file extension must be ".d.ts" and not ".ts".
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
*/
"mainEntryPointFilePath": "<projectFolder>/dist/typings-temp/index.d.ts",
/**
* A list of NPM package names whose exports should be treated as part of this package.
*
* For example, suppose that Webpack is used to generate a distributed bundle for the project "library1",
* and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part
* of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly
* imports library2. To avoid this, we can specify:
*
* "bundledPackages": [ "library2" ],
*
* This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been
* local files for library1.
*/
"bundledPackages": [],
/**
* Determines how the TypeScript compiler engine will be invoked by API Extractor.
*/
"compiler": {
/**
* Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* Note: This setting will be ignored if "overrideTsconfig" is used.
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/tsconfig.json"
*/
// "tsconfigFilePath": "<projectFolder>/tsconfig.json",
/**
* Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk.
* The object must conform to the TypeScript tsconfig schema:
*
* http://json.schemastore.org/tsconfig
*
* If omitted, then the tsconfig.json file will be read from the "projectFolder".
*
* DEFAULT VALUE: no overrideTsconfig section
*/
// "overrideTsconfig": {
// . . .
// }
/**
* This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended
* and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when
* dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses
* for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck.
*
* DEFAULT VALUE: false
*/
// "skipLibCheck": true,
},
/**
* Configures how the API report file (*.api.md) will be generated.
*/
"apiReport": {
/**
* (REQUIRED) Whether to generate an API report.
*/
"enabled": true
/**
* The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce
* a full file path.
*
* The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/".
*
* SUPPORTED TOKENS: <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<unscopedPackageName>.api.md"
*/
// "reportFileName": "<unscopedPackageName>.api.md",
/**
* Specifies the folder where the API report file is written. The file name portion is determined by
* the "reportFileName" setting.
*
* The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy,
* e.g. for an API review.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/etc/"
*/
// "reportFolder": "<projectFolder>/etc/",
/**
* Specifies the folder where the temporary report file is written. The file name portion is determined by
* the "reportFileName" setting.
*
* After the temporary file is written to disk, it is compared with the file in the "reportFolder".
* If they are different, a production build will fail.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/temp/"
*/
// "reportTempFolder": "<projectFolder>/temp/"
},
/**
* Configures how the doc model file (*.api.json) will be generated.
*/
"docModel": {
/**
* (REQUIRED) Whether to generate a doc model file.
*/
"enabled": true
/**
* The output path for the doc model file. The file extension should be ".api.json".
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/temp/<unscopedPackageName>.api.json"
*/
// "apiJsonFilePath": "<projectFolder>/temp/<unscopedPackageName>.api.json"
},
/**
* Configures how the .d.ts rollup file will be generated.
*/
"dtsRollup": {
/**
* (REQUIRED) Whether to generate the .d.ts rollup file.
*/
"enabled": true,
/**
* Specifies the output path for a .d.ts rollup file to be generated without any trimming.
* This file will include all declarations that are exported by the main entry point.
*
* If the path is an empty string, then this file will not be written.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/dist/<unscopedPackageName>.d.ts"
*/
"untrimmedFilePath": "<projectFolder>/dist/typing/index.d.ts"
/**
* Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release.
* This file will include only declarations that are marked as "@public" or "@beta".
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: ""
*/
// "betaTrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>-beta.d.ts",
/**
* Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release.
* This file will include only declarations that are marked as "@public".
*
* If the path is an empty string, then this file will not be written.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: ""
*/
// "publicTrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>-public.d.ts",
/**
* When a declaration is trimmed, by default it will be replaced by a code comment such as
* "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the
* declaration completely.
*
* DEFAULT VALUE: false
*/
// "omitTrimmingComments": true
},
/**
* Configures how the tsdoc-metadata.json file will be generated.
*/
"tsdocMetadata": {
/**
* Whether to generate the tsdoc-metadata.json file.
*
* DEFAULT VALUE: true
*/
// "enabled": true,
/**
* Specifies where the TSDoc metadata file should be written.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* The default value is "<lookup>", which causes the path to be automatically inferred from the "tsdocMetadata",
* "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup
* falls back to "tsdoc-metadata.json" in the package folder.
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<lookup>"
*/
// "tsdocMetadataFilePath": "<projectFolder>/dist/tsdoc-metadata.json"
},
/**
* Specifies what type of newlines API Extractor should use when writing output files. By default, the output files
* will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead.
* To use the OS's default newline kind, specify "os".
*
* DEFAULT VALUE: "crlf"
*/
// "newlineKind": "crlf",
/**
* Configures how API Extractor reports error and warning messages produced during analysis.
*
* There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages.
*/
"messages": {
/**
* Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing
* the input .d.ts files.
*
* TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551"
*
* DEFAULT VALUE: A single "default" entry with logLevel=warning.
*/
"compilerMessageReporting": {
/**
* Configures the default routing for messages that don't match an explicit rule in this table.
*/
"default": {
/**
* Specifies whether the message should be written to the the tool's output log. Note that
* the "addToApiReportFile" property may supersede this option.
*
* Possible values: "error", "warning", "none"
*
* Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail
* and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes
* the "--local" option), the warning is displayed but the build will not fail.
*
* DEFAULT VALUE: "warning"
*/
"logLevel": "warning"
/**
* When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md),
* then the message will be written inside that file; otherwise, the message is instead logged according to
* the "logLevel" option.
*
* DEFAULT VALUE: false
*/
// "addToApiReportFile": false
}
// "TS2551": {
// "logLevel": "warning",
// "addToApiReportFile": true
// },
//
// . . .
},
/**
* Configures handling of messages reported by API Extractor during its analysis.
*
* API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag"
*
* DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings
*/
"extractorMessageReporting": {
"default": {
"logLevel": "warning"
// "addToApiReportFile": false
}
// "ae-extra-release-tag": {
// "logLevel": "warning",
// "addToApiReportFile": true
// },
//
// . . .
},
/**
* Configures handling of messages reported by the TSDoc parser when analyzing code comments.
*
* TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text"
*
* DEFAULT VALUE: A single "default" entry with logLevel=warning.
*/
"tsdocMessageReporting": {
"default": {
"logLevel": "warning"
// "addToApiReportFile": false
}
// "tsdoc-link-tag-unescaped-text": {
// "logLevel": "warning",
// "addToApiReportFile": true
// },
//
// . . .
}
}
}package.json{
...
"scripts": {
"build": "shx rm -rf dist/** && npm run build:umd && npm run build:lib-esm && npm run build:extract-api",
"build:umd": "webpack -c webpack.config.ts --node-env production --env NODE_ENV=production",
"build:lib-esm": "tsc -p tsconfig.json --declarationDir ./dist/typings-temp -m es6 --outDir dist/lib-esm",
"build:extract-api": "api-extractor run && shx rm -rf dist/typings-temp",
"build:extract-api-local": "shx mkdir -p ./etc && npm run build:lib-esm && api-extractor run -l",
"test": "npm run test"
},
...
}注意, 这里处理新增了一个build:extract-api到scripts配置中, 还修改了build:lib-esm的配置, 将其输出的typescript类型声明文件放到了, typings-temp目录中, 最后这个目录是要删除; 还要注意, 每次提交代码到版本管理工具前, 要先运行npm run build:extract-api-local, 这个命令会生成./etc/<libraryName>.api.md文件, 这个文件是api-extractor生成的api文档, 应该要放到版本管理工具中去的, 以便可以看到每次提交代码时API的变化.
比如, 我希望Bar类不能被此库的使用者使用, 我可以加上下面这段注释
/**
*
* @internal
*/
export class Bar {
bar() {}
}
然后来看看生成的index.d.ts文件:
/**
*
* @internal
*/
declare class Bar {
bar(): void;
}
export declare class Foo {
private _bar;
constructor(_bar?: Bar);
foo(): void;
loaf(): void;
}
export { }
可以看出index.d.ts文件中虽然declare了Bar, 但是并未导出Bar
这个特性是由api-extractor提供的, 更多api-extractor的内容移步官方文档
至此, 我们就可以构建一个可以通过诸如AMD CommonJs esm等js模块系统或是使用script标签的方式引用的js库了, 主要用到了webpack typescript api-extractor这些工具. 完整的示例代码可以访问github-laggage/loaf查看.
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?
我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案
我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的rubyyaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir
我的问题的一个例子是体育游戏。一场体育比赛有两支球队,一支主队和一支客队。我的事件记录模型如下:classTeam"Team"has_one:away_team,:class_name=>"Team"end我希望能够通过游戏访问一个团队,例如:Game.find(1).home_team但我收到一个单元化常量错误:Game::team。谁能告诉我我做错了什么?谢谢, 最佳答案 如果Gamehas_one:team那么Rails假设您的teams表有一个game_id列。不过,您想要的是games表有一个team_id列,在这种情况下