阿里云部署尝试
49
.fun/tmp/deploy/template.yml
Normal file
@ -0,0 +1,49 @@
|
||||
ROSTemplateFormatVersion: '2015-09-01'
|
||||
Transform: 'Aliyun::Serverless-2018-04-03'
|
||||
Resources:
|
||||
llej-service:
|
||||
Type: 'Aliyun::Serverless::Service'
|
||||
Properties:
|
||||
Role: >-
|
||||
acs:ram::1196182666067174:role/aliyunfcgeneratedrole-cn-hangzhou-llej-service
|
||||
LogConfig:
|
||||
Project: llej-hd1
|
||||
Logstore: llej-hd1
|
||||
VpcConfig:
|
||||
VpcId: vpc-bp1fu2excjgage7cjehom
|
||||
VSwitchIds:
|
||||
- vsw-bp1ko07xl7lwnjoftuobf
|
||||
SecurityGroupId: sg-bp1aiw8pa6se0mo5iuw7
|
||||
NasConfig:
|
||||
UserId: 10003
|
||||
GroupId: 10003
|
||||
MountPoints:
|
||||
- ServerAddr: '089ab49e4d-jty6.cn-hangzhou.nas.aliyuncs.com:/llej-service'
|
||||
MountDir: /mnt/auto
|
||||
InternetAccess: true
|
||||
webFont:
|
||||
Type: 'Aliyun::Serverless::Function'
|
||||
Properties:
|
||||
Handler: dist/src/index.handler
|
||||
Runtime: nodejs10
|
||||
Timeout: 60
|
||||
MemorySize: 128
|
||||
InstanceConcurrency: 1
|
||||
EnvironmentVariables:
|
||||
LD_LIBRARY_PATH: >-
|
||||
/code/.fun/root/usr/local/lib:/code/.fun/root/usr/lib:/code/.fun/root/usr/lib/x86_64-linux-gnu:/code/.fun/root/usr/lib64:/code/.fun/root/lib:/code/.fun/root/lib/x86_64-linux-gnu:/code/.fun/root/python/lib/python2.7/site-packages:/code/.fun/root/python/lib/python3.6/site-packages:/code:/code/lib:/usr/local/lib:/mnt/auto/root/usr/local/lib:/mnt/auto/root/usr/lib:/mnt/auto/root/usr/lib/x86_64-linux-gnu:/mnt/auto/root/usr/lib64:/mnt/auto/root/lib:/mnt/auto/root/lib/x86_64-linux-gnu:/mnt/auto/root/python/lib/python2.7/site-packages:/mnt/auto/root/python/lib/python3.6/site-packages
|
||||
NODE_PATH: >-
|
||||
/mnt/auto/node_modules:/usr/local/lib/node_modules:/code/node_modules
|
||||
PATH: >-
|
||||
/code/.fun/root/usr/local/bin:/code/.fun/root/usr/local/sbin:/code/.fun/root/usr/bin:/code/.fun/root/usr/sbin:/code/.fun/root/sbin:/code/.fun/root/bin:/code:/code/node_modules/.bin:/code/.fun/python/bin:/code/.fun/node_modules/.bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin:/mnt/auto/root/usr/local/bin:/mnt/auto/root/usr/local/sbin:/mnt/auto/root/usr/bin:/mnt/auto/root/usr/sbin:/mnt/auto/root/sbin:/mnt/auto/root/bin:/mnt/auto/python/bin:/mnt/auto/node_modules/.bin
|
||||
PYTHONPATH: >-
|
||||
/mnt/auto/python/lib/python2.7/site-packages:/mnt/auto/python/lib/python3.6/site-packages
|
||||
PYTHONUSERBASE: /code/.fun/python
|
||||
Events:
|
||||
httpTrigger:
|
||||
Type: HTTP
|
||||
Properties:
|
||||
AuthType: anonymous
|
||||
Methods:
|
||||
- POST
|
||||
- GET
|
||||
39
.gitignore
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
.cache
|
||||
dist
|
||||
asset/font
|
||||
asset/dynamically
|
||||
4
.nas.yml
Normal file
@ -0,0 +1,4 @@
|
||||
nasMappings:
|
||||
llej-service:
|
||||
- localNasDir: node_modules
|
||||
remoteNasDir: /mnt/auto/node_modules
|
||||
4
.prettierrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
25
.template.yml.backup
Normal file
@ -0,0 +1,25 @@
|
||||
ROSTemplateFormatVersion: '2015-09-01'
|
||||
Transform: 'Aliyun::Serverless-2018-04-03'
|
||||
Resources:
|
||||
llej-service:
|
||||
Type: 'Aliyun::Serverless::Service'
|
||||
Properties:
|
||||
Description: ''
|
||||
NasConfig: Auto
|
||||
webFont:
|
||||
Type: 'Aliyun::Serverless::Function'
|
||||
Properties:
|
||||
Handler: dist/index.handler
|
||||
Runtime: nodejs10
|
||||
CodeUri: ./
|
||||
Timeout: 60
|
||||
EnvironmentVariables:
|
||||
NODE_PATH: '/mnt/auto/node_modules:/usr/local/lib/node_modules'
|
||||
Events:
|
||||
httpTrigger:
|
||||
Type: HTTP
|
||||
Properties:
|
||||
AuthType: ANONYMOUS
|
||||
Methods:
|
||||
- POST
|
||||
- GET
|
||||
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"deno.enable": false
|
||||
}
|
||||
103
README.md
Normal file
@ -0,0 +1,103 @@
|
||||
# web font 字体裁剪工具
|
||||
|
||||
## 起因
|
||||
|
||||
ui 需要展现一些特定的字体,但直接引入字体包又过大,于是想到了裁剪字体,一开始想的使用「字蛛」但他是针对静态网站的,而且实际他会多出许多英文的,估计是直接将源码中存在的文字都算进去了。
|
||||
后来又找到阿里的「webfont」 但他的字体有限,项目又不开源,所以自己写了这个
|
||||
|
||||
## 尝试
|
||||
|
||||
[web font 在线站点](https://webfont.shenzilong.cn/)
|
||||
|
||||
请注意,由于这个服务器比较差,所以访问可能比较慢,且因为服务器空间问题我会不定时的清空生成的资源,所以请不要使用这个站点生成的在线资源,如有需要应当自行布设
|
||||
|
||||
## 目的与功能
|
||||
|
||||
1.裁剪字体包使其仅包含选中的字体
|
||||
|
||||
例如 如下图生成的字体包仅包含 「天地无极乾坤借法」
|
||||

|
||||
|
||||
<video src="./doc_img/功能演示.mkv" controls="controls" width:100% height:auto></video>
|
||||
|
||||
其体积自然十分之小
|
||||
|
||||

|
||||
|
||||
2.另外可以生成 css 直接复制可用,部署在公网便可永久访问
|
||||
|
||||
例如
|
||||
|
||||
```css
|
||||
@font-face {
|
||||
font-family: "QIJIC";
|
||||
src: url("http://127.0.0.1:3000/asset/font/1584680576469/令东齐伋复刻体.eot"); /* IE9 */
|
||||
src: url("http://127.0.0.1:3000/asset/font/1584680576469/令东齐伋复刻体.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
|
||||
url("http://127.0.0.1:3000/asset/font/1584680576469/令东齐伋复刻体.woff") format("woff"), /* chrome, firefox */
|
||||
url("http://127.0.0.1:3000/asset/font/1584680576469/令东齐伋复刻体.ttf") format("truetype"), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||
url("http://127.0.0.1:3000/asset/font/1584680576469/令东齐伋复刻体.svg#QIJIC") format("svg"); /* iOS 4.1- */
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
}
|
||||
```
|
||||
|
||||
3.将 ttf 的字体包放置在 ./asset/font_src/ 目录下自然可以检测到新的可用字体,无需重启服务
|
||||
|
||||

|
||||
|
||||
4.提供 zip 的整体下载方案
|
||||
|
||||

|
||||
|
||||
## 提供的服务
|
||||
|
||||
### 查询可用字体列表
|
||||
|
||||

|
||||
|
||||
### 生成压缩字体包
|
||||
|
||||

|
||||
|
||||
如图可见每个返回的字体资源,访问即可下载。另外在访问该目录下的 asset.zip 可以直接下载全部的文件,生成的资源目录结构见下图
|
||||
|
||||

|
||||
|
||||
注意,此接口是还支持 post 方式访问的,这样可以一次请求多个类型的字体文件,而且不会如同 get 方法那样有长度限制
|
||||
|
||||
|
||||

|
||||
|
||||
### 动态生成字体
|
||||
|
||||

|
||||
|
||||
#### 请注意
|
||||
|
||||
只支持生成 .ttf .eot .woff .svg 这几种格式
|
||||
|
||||
## 写项目时遇到的问题
|
||||
|
||||
1. 使用 svelte https://github.com/DeMoorJasper/parcel-plugin-svelte 通过这个插件使用 parcel 然后报 new 的错 需要限制 编译的版本,在package.json browserslist 字段限制一下版本就好
|
||||
|
||||
2. parcel 对 post purgecss 支持好像有问题,需要修改 postcss.config.js 文件他才能正确的删除样式
|
||||
|
||||
## 启动
|
||||
|
||||
```bash
|
||||
npm i
|
||||
npm run build
|
||||
npm run start
|
||||
```
|
||||
|
||||
默认的访问地址是 http://127.0.0.1:3000
|
||||
|
||||
## 鸣谢
|
||||
|
||||
[字体天下](http://www.fonts.net.cn/commercial-free-32767/fonts-zh-1.html)
|
||||
|
||||
[fontmin](https://github.com/ecomfe/fontmin)
|
||||
|
||||
## License
|
||||
|
||||
MIT © [崮生](https://shenzilong.cn/关于/mit.html)
|
||||
BIN
asset/font_src/Alibaba-PuHuiTi-Heavy.ttf
Normal file
BIN
asset/font_src/令东齐伋复刻体.ttf
Normal file
BIN
asset/font_src/优设标题黑.ttf
Normal file
BIN
asset/font_src/问藏书房.ttf
Normal file
82
build/config.gypi
Normal file
@ -0,0 +1,82 @@
|
||||
# Do not edit. File was generated by node-gyp's "configure" step
|
||||
{
|
||||
"target_defaults": {
|
||||
"cflags": [],
|
||||
"default_configuration": "Release",
|
||||
"defines": [],
|
||||
"include_dirs": [],
|
||||
"libraries": [],
|
||||
"msbuild_toolset": "v142",
|
||||
"msvs_windows_target_platform_version": "10.0.18362.0"
|
||||
},
|
||||
"variables": {
|
||||
"asan": 0,
|
||||
"build_v8_with_gn": "false",
|
||||
"coverage": "false",
|
||||
"dcheck_always_on": 0,
|
||||
"debug_nghttp2": "false",
|
||||
"debug_node": "false",
|
||||
"enable_lto": "false",
|
||||
"enable_pgo_generate": "false",
|
||||
"enable_pgo_use": "false",
|
||||
"error_on_warn": "false",
|
||||
"force_dynamic_crt": 0,
|
||||
"host_arch": "x64",
|
||||
"icu_data_in": "..\\..\\deps\\icu-tmp\\icudt67l.dat",
|
||||
"icu_endianness": "l",
|
||||
"icu_gyp_path": "tools/icu/icu-generic.gyp",
|
||||
"icu_path": "deps/icu-small",
|
||||
"icu_small": "false",
|
||||
"icu_ver_major": "67",
|
||||
"is_debug": 0,
|
||||
"napi_build_version": "6",
|
||||
"nasm_version": "2.14",
|
||||
"node_byteorder": "little",
|
||||
"node_debug_lib": "false",
|
||||
"node_enable_d8": "false",
|
||||
"node_install_npm": "true",
|
||||
"node_module_version": 83,
|
||||
"node_no_browser_globals": "false",
|
||||
"node_prefix": "/usr/local",
|
||||
"node_release_urlbase": "https://nodejs.org/download/release/",
|
||||
"node_shared": "false",
|
||||
"node_shared_brotli": "false",
|
||||
"node_shared_cares": "false",
|
||||
"node_shared_http_parser": "false",
|
||||
"node_shared_libuv": "false",
|
||||
"node_shared_nghttp2": "false",
|
||||
"node_shared_openssl": "false",
|
||||
"node_shared_zlib": "false",
|
||||
"node_tag": "",
|
||||
"node_target_type": "executable",
|
||||
"node_use_bundled_v8": "true",
|
||||
"node_use_dtrace": "false",
|
||||
"node_use_etw": "true",
|
||||
"node_use_node_code_cache": "true",
|
||||
"node_use_node_snapshot": "true",
|
||||
"node_use_openssl": "true",
|
||||
"node_use_v8_platform": "true",
|
||||
"node_with_ltcg": "true",
|
||||
"node_without_node_options": "false",
|
||||
"openssl_fips": "",
|
||||
"openssl_is_fips": "false",
|
||||
"shlib_suffix": "so.83",
|
||||
"target_arch": "x64",
|
||||
"v8_enable_31bit_smis_on_64bit_arch": 0,
|
||||
"v8_enable_gdbjit": 0,
|
||||
"v8_enable_i18n_support": 1,
|
||||
"v8_enable_inspector": 1,
|
||||
"v8_enable_lite_mode": 0,
|
||||
"v8_enable_pointer_compression": 0,
|
||||
"v8_no_strict_aliasing": 1,
|
||||
"v8_optimized_debug": 1,
|
||||
"v8_promise_internal_field_count": 1,
|
||||
"v8_random_seed": 0,
|
||||
"v8_trace_maps": 0,
|
||||
"v8_use_siphash": 1,
|
||||
"want_separate_host_toolset": 0,
|
||||
"nodedir": "C:\\Users\\llej\\AppData\\Local\\node-gyp\\Cache\\14.5.0",
|
||||
"standalone_static_library": 1,
|
||||
"msbuild_path": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe"
|
||||
}
|
||||
}
|
||||
BIN
doc_img/api/font_list.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
doc_img/api/fontmin.jpg
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
doc_img/api/fontmin_post.jpg
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
doc_img/api/generate_fonts_dynamically.jpg
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
doc_img/下载展示.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
doc_img/体积展示.jpg
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
doc_img/功能演示.mkv
Normal file
BIN
doc_img/生成的资源.jpg
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
doc_img/路径展示.jpg
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
doc_img/页面截图.jpg
Normal file
|
After Width: | Height: | Size: 75 KiB |
4
nest-cli.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src"
|
||||
}
|
||||
15501
package-lock.json
generated
Normal file
78
package.json
Normal file
@ -0,0 +1,78 @@
|
||||
{
|
||||
"name": "webfont",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "nest build && parcel build ./static/index.html",
|
||||
"build:font": "nest build && parcel build ./static/index.html --public-url /font/",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start:web": "parcel ./static/index.html",
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "tslint -p tsconfig.json -c tslint.json",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.8.7",
|
||||
"@fullhuman/postcss-purgecss": "^2.1.0",
|
||||
"@nestjs/common": "^6.7.2",
|
||||
"@nestjs/core": "^6.7.2",
|
||||
"@nestjs/platform-express": "^6.7.2",
|
||||
"@nestjs/serve-static": "^2.1.0",
|
||||
"@webserverless/fc-express": "^0.1.4",
|
||||
"font-spider": "^1.3.5",
|
||||
"fontmin": "^0.9.8",
|
||||
"parcel-bundler": "^1.12.4",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.0",
|
||||
"rxjs": "^6.6.0",
|
||||
"svelte": "^3.20.1",
|
||||
"tailwindcss": "^1.2.0",
|
||||
"zip-a-folder": "0.0.12",
|
||||
"zip-folder": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^6.9.0",
|
||||
"@nestjs/schematics": "^6.7.0",
|
||||
"@nestjs/testing": "^6.7.1",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/jest": "^24.0.18",
|
||||
"@types/node": "^12.7.5",
|
||||
"@types/supertest": "^2.0.10",
|
||||
"cssnano": "^4.1.10",
|
||||
"jest": "^24.9.0",
|
||||
"parcel-plugin-svelte": "^4.0.6",
|
||||
"prettier": "^1.18.2",
|
||||
"supertest": "^4.0.2",
|
||||
"ts-jest": "^24.1.0",
|
||||
"ts-loader": "^6.2.2",
|
||||
"ts-node": "^8.10.2",
|
||||
"tsconfig-paths": "^3.9.0",
|
||||
"tslint": "^5.20.0",
|
||||
"typescript": "^3.9.6"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".spec.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
},
|
||||
"browserslist": "last 10 chrome versions"
|
||||
}
|
||||
23
postcss.config.js
Normal file
@ -0,0 +1,23 @@
|
||||
const tailwindcss = require('tailwindcss');
|
||||
|
||||
const purgecss = require('@fullhuman/postcss-purgecss')({
|
||||
// Specify the paths to all of the template files in your project
|
||||
content: [
|
||||
'./static/**/*.svelte',
|
||||
'./static/**/*.js',
|
||||
'./static/**/*.ts',
|
||||
'./static/*.svelte',
|
||||
'./static/*.js',
|
||||
'./static/*.ts',
|
||||
`111 `
|
||||
],
|
||||
|
||||
// Include any special characters you're using in this regular expression
|
||||
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || [],
|
||||
});
|
||||
module.exports = {
|
||||
plugins: [
|
||||
tailwindcss,
|
||||
...(process.env.NODE_ENV === 'development' ? [] : [purgecss]),
|
||||
],
|
||||
};
|
||||
22
src/app.controller.spec.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppController', () => {
|
||||
let appController: AppController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const app: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
|
||||
appController = app.get<AppController>(AppController);
|
||||
});
|
||||
|
||||
describe('root', () => {
|
||||
it('should return "Hello World!"', () => {
|
||||
expect(appController.getHello()).toBe('Hello World!');
|
||||
});
|
||||
});
|
||||
});
|
||||
85
src/app.controller.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Query,
|
||||
Response,
|
||||
Res,
|
||||
Req,
|
||||
Post,
|
||||
Body,
|
||||
} from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
import { join } from 'path';
|
||||
import { promises as fs } from 'fs';
|
||||
import { Request } from 'express';
|
||||
import { Stream } from 'stream';
|
||||
import { req_par } from './req.decorator';
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
/** 压缩字体 */
|
||||
@Get('fontmin')
|
||||
font_min(@Query('text') text, @Query('font') font,@req_par('host_url') host_url:string) {
|
||||
|
||||
return this.appService.font_min(text, font,host_url);
|
||||
}
|
||||
@Post('fontmin')
|
||||
font_min_post(@Body() body: { text: string; font: string }[],@req_par('host_url') host_url:string) {
|
||||
const res = body.map(par => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.appService
|
||||
.font_min(par.text, par.font,host_url)
|
||||
.then(r => {
|
||||
resolve({
|
||||
font: par.font,
|
||||
css:r,
|
||||
status: 'success',
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
resolve({
|
||||
font: par.font,
|
||||
status: 'failure',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return Promise.all(res);
|
||||
}
|
||||
|
||||
/** 返回字体列表 */
|
||||
@Get('font_list')
|
||||
font_list() {
|
||||
return this.appService.font_list();
|
||||
}
|
||||
|
||||
/** 压缩字体 */
|
||||
@Get('generate_fonts_dynamically*')
|
||||
async generate_fonts_dynamically(
|
||||
@Req() req: Request,
|
||||
@Res() res,
|
||||
@Query('text') text: string,
|
||||
@Query('font') font: string,
|
||||
@Query('temp') temp: string,
|
||||
) {
|
||||
const type = req.url.match(/\.(.*)\?/)[1];
|
||||
res.set({
|
||||
'Content-Type': `font/${type}`,
|
||||
});
|
||||
if (!text) return ' ';
|
||||
const file = await this.appService.generate_fonts_dynamically(
|
||||
text,
|
||||
font,
|
||||
temp,
|
||||
type,
|
||||
);
|
||||
|
||||
const bufferStream = new Stream.PassThrough();
|
||||
bufferStream.end(file);
|
||||
bufferStream.pipe(res);
|
||||
}
|
||||
}
|
||||
|
||||
function promise_execute_all<T>(params: Promise<T>[]) {}
|
||||
21
src/app.module.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||
import { join } from 'path';
|
||||
console.log( join(__dirname, '..'));
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ServeStaticModule.forRoot({
|
||||
rootPath: join(__dirname, '..'),
|
||||
}),
|
||||
ServeStaticModule.forRoot({
|
||||
rootPath: join(__dirname, '../../asset'),
|
||||
serveRoot: '/asset',
|
||||
}),
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
})
|
||||
export class AppModule {}
|
||||
142
src/app.service.ts
Normal file
@ -0,0 +1,142 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import crypto from 'crypto';
|
||||
import Fontmin from 'fontmin';
|
||||
import { promises as fs } from 'fs';
|
||||
import { join } from 'path';
|
||||
const { zip } = require('zip-a-folder');
|
||||
|
||||
const font_src = './asset/font_src/';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
font_list() {
|
||||
const font_dir = join(__dirname, `../../${font_src}`);
|
||||
return fs.readdir(font_dir);
|
||||
}
|
||||
async font_min(text: string, font: string, server_url: string) {
|
||||
/** 因为 text 为 空或者是空格之类的 会导致 fontmin 运算很久 */
|
||||
text += '●';
|
||||
const srcPath = `${font_src}${font}.ttf`; // 字体源文件
|
||||
|
||||
const outPath =
|
||||
(process.env.is_aliyun ? '/tmp/' : '') + `asset/font/${Date.now()}/`;
|
||||
const destPath = (process.env.is_aliyun ? '/tmp/' : '.') + `/${outPath}`; // 输出路径
|
||||
// 初始化
|
||||
const fontmin = new Fontmin()
|
||||
.src(srcPath) // 输入配置
|
||||
.use(
|
||||
Fontmin.glyph({
|
||||
text, // 所需文字
|
||||
}),
|
||||
)
|
||||
.use(Fontmin.ttf2eot()) // eot 转换插件
|
||||
.use(Fontmin.ttf2woff()) // woff 转换插件
|
||||
.use(Fontmin.ttf2svg()) // svg 转换插件
|
||||
.use(Fontmin.css({ fontPath: `${server_url}${outPath}` })) // css 生成插件
|
||||
.dest(destPath); // 输出配置
|
||||
|
||||
// 执行
|
||||
return new Promise((resolve, reject) => {
|
||||
fontmin.run(function(err, files, stream) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
|
||||
reject(err);
|
||||
} else {
|
||||
const css = files
|
||||
.filter(f =>
|
||||
(f.history[f.history.length - 1] as string).endsWith('.css'),
|
||||
)
|
||||
.map(f => f._contents.toString())[0];
|
||||
zip(
|
||||
join(__dirname, '../../', destPath),
|
||||
join(__dirname, '../../', destPath, 'asset.zip'),
|
||||
);
|
||||
// resolve({code:0,fil:files.map(f=>f._contents.toString())}); // 成功
|
||||
// resolve({code:0,files}); // 成功
|
||||
resolve(css); // 成功
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async generate_fonts_dynamically(
|
||||
text: string,
|
||||
font: string,
|
||||
temp: string,
|
||||
type: string,
|
||||
) {
|
||||
text += '●';
|
||||
const hash = crypto.createHash('md5');
|
||||
hash.update(`${type}${font}${text}`);
|
||||
const hash_str = hash.digest('hex');
|
||||
const srcPath = `${font_src}${font}.ttf`; // 字体源文件
|
||||
const outPath = `asset/dynamically/${hash_str}`;
|
||||
const destPath = `./${outPath}`; // 输出路径
|
||||
|
||||
const full_path = join(__dirname, '../../', destPath, `${font}.${type}`);
|
||||
/** 需要持久化 */
|
||||
if (temp !== 'true') {
|
||||
try {
|
||||
return await fs.readFile(full_path);
|
||||
} catch (error) {
|
||||
console.log(`开始生成 ${full_path}`, error);
|
||||
}
|
||||
}
|
||||
// 初始化
|
||||
const fontmin = new Fontmin()
|
||||
.src(srcPath) // 输入配置
|
||||
.use(
|
||||
Fontmin.glyph({
|
||||
text, // 所需文字
|
||||
}),
|
||||
);
|
||||
|
||||
if ('eot' === type) {
|
||||
fontmin.use(Fontmin.ttf2eot()); // eot 转换插件
|
||||
}
|
||||
if ('woff' === type) {
|
||||
fontmin.use(Fontmin.ttf2woff()); // eot 转换插件
|
||||
}
|
||||
if ('svg' === type) {
|
||||
fontmin.use(Fontmin.ttf2svg()); // eot 转换插件
|
||||
}
|
||||
/** 缓存数据 */
|
||||
if (temp !== 'true') {
|
||||
fontmin.dest(destPath);
|
||||
}
|
||||
|
||||
// 执行
|
||||
return new Promise((resolve, reject) => {
|
||||
fontmin.run(async function(err, files, stream) {
|
||||
if (err) {
|
||||
// 异常捕捉
|
||||
reject(err);
|
||||
} else {
|
||||
const buffer = files.filter(f =>
|
||||
/** 筛选需要的类型 */
|
||||
(f.history[f.history.length - 1] as string).endsWith(type),
|
||||
)[0]._contents;
|
||||
resolve(buffer); // 成功
|
||||
/** 存个日志 */
|
||||
if (temp !== 'true') {
|
||||
const content_path = join(
|
||||
__dirname,
|
||||
'../../',
|
||||
destPath,
|
||||
`content.txt`,
|
||||
);
|
||||
try {
|
||||
await fs.appendFile(
|
||||
content_path,
|
||||
`type:${type}\nfont:${font}\ntext:${text}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(`写 ${content_path} 失败`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
3
src/config.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const config={
|
||||
web_font_path:"//127.0.0.1:3000/"
|
||||
}
|
||||
24
src/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { ExpressAdapter } from '@nestjs/platform-express';
|
||||
import { Server } from '@webserverless/fc-express';
|
||||
import express from 'express';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
const app = express();
|
||||
|
||||
let p = (async () => {
|
||||
const adapter = new ExpressAdapter(app);
|
||||
const app2 = await NestFactory.create(AppModule, adapter);
|
||||
app2.enableCors();
|
||||
await app2.init();
|
||||
})();
|
||||
app.use(function(req, res, next) {
|
||||
console.log(`▩▩▩[time:${Date.now()} [url:${req.url}]]`);
|
||||
p.then(() => next());
|
||||
});
|
||||
|
||||
const server = new Server(app);
|
||||
|
||||
module.exports.handler = function(req, res, context) {
|
||||
server.httpProxy(req, res, context);
|
||||
};
|
||||
15
src/main.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import serveStatic from 'serve-static';
|
||||
import { Response } from 'express';
|
||||
import { logger } from './middleware/logger.middleware';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
app.enableCors();
|
||||
app.use(logger);
|
||||
|
||||
await app.listen(3000);
|
||||
}
|
||||
bootstrap();
|
||||
|
||||
26
src/middleware/logger.middleware.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Response, Request } from 'express';
|
||||
|
||||
const NS_PER_SEC = 1e9;
|
||||
export async function logger(req: Request, res: Response, next) {
|
||||
const time = process.hrtime();
|
||||
next();
|
||||
res.once('finish', () => {
|
||||
const diff = process.hrtime(time);
|
||||
console.log(
|
||||
`[${req.headers['x-forwarded-for'] ||
|
||||
req.connection.remoteAddress ||
|
||||
req.socket.remoteAddress ||
|
||||
req.connection.remoteAddress}][${(diff[0] * NS_PER_SEC + diff[1]) /
|
||||
1000000}]`,
|
||||
decodeURI_catch(req.url),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function decodeURI_catch(url: string) {
|
||||
try {
|
||||
return decodeURI(url);
|
||||
} catch (error) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
9
src/req.decorator.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { createParamDecorator } from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
|
||||
export const req_par = createParamDecorator((data: string, req:Request) => {
|
||||
if('host_url'===data){
|
||||
return `//${req.headers.host}/`
|
||||
}
|
||||
return req
|
||||
});
|
||||
140
static/App.svelte
Normal file
@ -0,0 +1,140 @@
|
||||
<script>
|
||||
import { writable } from 'svelte/store';
|
||||
import { get_font, get_font_list,server,post_fontmin } from './req';
|
||||
/** 可用的字体列表 {id:number,name:string:selected:undefined | boolen,css:undefined|string}*/
|
||||
$: font_list = [];
|
||||
get_font_list().then(r => {
|
||||
font_list = r.map(ttf => ({ name: ttf.replace(/\.ttf$/, '') }));
|
||||
});
|
||||
/** 选择的文字 */
|
||||
let text = '在此输入需要提取的文字\n在右侧选择字体\n然后点击下方的生成字体按钮';
|
||||
/** 请求方式 */
|
||||
let request_method="post"
|
||||
/** 用于测试动态生成接口 */
|
||||
let generate_fonts_dynamically=`<style>
|
||||
@font-face {
|
||||
font-family: "test";
|
||||
src:
|
||||
url("${server}generate_fonts_dynamically.ttf?temp=true&font=优设标题黑&text=优设标题黑(直接改这里和前面的字体名看效果)") format("truetype");
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
}
|
||||
</style>`
|
||||
$: selected_font = font_list.filter(font => font.selected);
|
||||
function generate_font() {
|
||||
if('post'===request_method){
|
||||
/** 使用 post 请求,单请求方式 */
|
||||
post_fontmin(
|
||||
selected_font.map(f=>({
|
||||
font:f.name, text
|
||||
}))
|
||||
).then(res=>{
|
||||
selected_font.forEach(font=>{
|
||||
let r=res.find(o=>o.font===font.name).css
|
||||
font_processing(font,r)
|
||||
})
|
||||
})
|
||||
}
|
||||
if('get'===request_method){
|
||||
/** 使用 get 请求,多请求方式 */
|
||||
selected_font.forEach(font => {
|
||||
get_font(font.name, text)
|
||||
.then(r => {
|
||||
font_processing(font,r)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function font_processing(font,r) {
|
||||
r=r.replace(/\/\/.*?\//g,server)
|
||||
|
||||
const family = r.match(/font-family: "(.*)"/)[1];
|
||||
font.css = r;
|
||||
font.family = family;
|
||||
font.zip=server+r.match(/(asset\/font\/\d+\/)/)[0]+'asset.zip'
|
||||
/** 因为要触发其他更新则必须对这个变量重新赋值 */
|
||||
font_list = font_list;
|
||||
}
|
||||
}
|
||||
function copy(str) {
|
||||
var input = document.getElementById("copy_box");
|
||||
input.value=str
|
||||
input.focus();
|
||||
input.setSelectionRange(0, -1); // 全选
|
||||
document.execCommand("copy")
|
||||
alert(`复制成功\n${str}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each font_list as font, i}
|
||||
{@html "<style>"+font.css+'.'+font.name+"{font-family:"+font.family+"}</style>"}
|
||||
{/each}
|
||||
<h1 class="text-lg text-center mb-3 font-bold">web font 字体裁剪工具</h1>
|
||||
<textarea id="copy_box" class="w-0 h-0 fixed -m-24" />
|
||||
<div class="flex justify-evenly">
|
||||
<textarea
|
||||
bind:value={text}
|
||||
class="border flex-1 m-1"
|
||||
placeholder="在此输入需要提取的文字 在右侧选择字体 然后点击下方的生成字体按钮"
|
||||
cols="40"
|
||||
rows="3" />
|
||||
<div class="flex-1 m-1 flex flex-wrap">
|
||||
{#each font_list as font, i}
|
||||
<div
|
||||
on:click={e => (font.selected = !font.selected)}
|
||||
class="c-label {font.selected ? 'c-label-selected' : ''}
|
||||
{font.name}">
|
||||
{font.name}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<div on:click={generate_font} class="bg-red-200 text-red-600 rounded-md px-2 hover:bg-red-400 hover:text-white duration-75 flex items-center shadow-md">
|
||||
生成字体
|
||||
</div>
|
||||
|
||||
<div class="flex border ml-2 items-end">
|
||||
<div
|
||||
on:click={e => request_method="post"}
|
||||
class="c-label {request_method==="post" ? 'c-label-selected' : ''}">
|
||||
使用 post 请求
|
||||
</div>
|
||||
<div
|
||||
on:click={e => request_method="get"}
|
||||
class="c-label {request_method==="get" ? 'c-label-selected' : ''}">
|
||||
使用 get 请求
|
||||
</div>
|
||||
<div class="text-sm">* 具体区别请打开控制台查看请求</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{#each selected_font as font, i}
|
||||
<div class={font.name}>
|
||||
<div style="font-size:2rem">{text}</div>
|
||||
<div class="flex justify-end items-center text-xs">
|
||||
{#if font.css}
|
||||
<a class="text-blue-400 underline" href="/{font.zip}">下载压缩资源</a>
|
||||
<div class="c-label mx-1 text-xs" on:click={copy(font.css)}>复制css</div>
|
||||
{/if}
|
||||
|
||||
<div>{font.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
|
||||
<h2 class="text-lg text-center my-3 font-bold"> 动态生成字体(generate_fonts_dynamically 接口)</h2>
|
||||
<p class="ml-1">使用如下的方式引入,则可以直接使用</p>
|
||||
<textarea
|
||||
bind:value={generate_fonts_dynamically}
|
||||
class="border flex-1 m-1 w-full text-lg"
|
||||
placeholder="在此输入需要提取的文字"
|
||||
rows="13"
|
||||
style="font-family:test;" />
|
||||
{@html generate_fonts_dynamically}
|
||||
|
||||
|
||||
7
static/app.css
Normal file
@ -0,0 +1,7 @@
|
||||
.c-label {
|
||||
@apply border m-1 rounded-md px-1 items-center h-6 text-sm;
|
||||
}
|
||||
|
||||
.c-label-selected {
|
||||
@apply bg-red-600 text-white;
|
||||
}
|
||||
3
static/index.css
Normal file
@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
19
static/index.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>web font</title>
|
||||
<link rel="stylesheet" href="./index.css">
|
||||
</head>
|
||||
|
||||
<body class="p-4">
|
||||
|
||||
<div class="c-app"></div>
|
||||
|
||||
<!-- This script tag points to the source of the JS file we want to load and bundle -->
|
||||
<script src="./index.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
6
static/index.js
Normal file
@ -0,0 +1,6 @@
|
||||
// import '@babel/polyfill';
|
||||
import App from './App.svelte';
|
||||
import "./app.css";
|
||||
new App({
|
||||
target: document.querySelector('.c-app'),
|
||||
});
|
||||
49
static/req.ts
Normal file
@ -0,0 +1,49 @@
|
||||
export const server = '//' + location.host + location.pathname;
|
||||
/** get 方式压缩字体 */
|
||||
export function get_font(font: string, text: string) {
|
||||
return new Promise((rs, re) => {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.addEventListener('readystatechange', function() {
|
||||
if (this.readyState === 4) {
|
||||
rs(this.responseText);
|
||||
}
|
||||
});
|
||||
xhr.open(
|
||||
'GET',
|
||||
`${server}fontmin?font=${encodeURIComponent(
|
||||
font,
|
||||
)}&text=${encodeURIComponent(text)}`,
|
||||
);
|
||||
xhr.onerror = re;
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
/** post 方式压缩字体 */
|
||||
export function post_fontmin(par:{font:string,text:string}[]) {
|
||||
return new Promise((rs, re) => {
|
||||
var data = JSON.stringify(par);
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.addEventListener('readystatechange', function() {
|
||||
if (this.readyState === 4) {
|
||||
rs(JSON.parse(this.responseText) );
|
||||
}
|
||||
});
|
||||
xhr.open('POST', `${server}fontmin`);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.onerror = re;
|
||||
xhr.send(data);
|
||||
});
|
||||
}
|
||||
export function get_font_list(font: string, text: string) {
|
||||
return new Promise((rs, re) => {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.addEventListener('readystatechange', function() {
|
||||
if (this.readyState === 4) {
|
||||
rs(JSON.parse(this.responseText));
|
||||
}
|
||||
});
|
||||
xhr.open('GET', `${server}font_list`);
|
||||
xhr.onerror = re;
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
3
static/svelte.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
declare module '*.svelte' {
|
||||
export default any;
|
||||
}
|
||||
6
tailwind.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
theme: {
|
||||
},
|
||||
variants: {},
|
||||
plugins: [],
|
||||
};
|
||||
41
template.yml
Normal file
@ -0,0 +1,41 @@
|
||||
ROSTemplateFormatVersion: '2015-09-01'
|
||||
Transform: 'Aliyun::Serverless-2018-04-03'
|
||||
Resources:
|
||||
llej-service:
|
||||
Type: 'Aliyun::Serverless::Service'
|
||||
Properties:
|
||||
Role: >-
|
||||
acs:ram::1196182666067174:role/aliyunfcgeneratedrole-cn-hangzhou-llej-service
|
||||
LogConfig:
|
||||
Project: llej-hd1
|
||||
Logstore: llej-hd1
|
||||
VpcConfig:
|
||||
VpcId: vpc-bp1fu2excjgage7cjehom
|
||||
VSwitchIds:
|
||||
- vsw-bp1ko07xl7lwnjoftuobf
|
||||
SecurityGroupId: sg-bp1aiw8pa6se0mo5iuw7
|
||||
NasConfig:
|
||||
UserId: 10003
|
||||
GroupId: 10003
|
||||
MountPoints:
|
||||
- ServerAddr: '089ab49e4d-jty6.cn-hangzhou.nas.aliyuncs.com:/llej-service'
|
||||
MountDir: /mnt/auto
|
||||
InternetAccess: true
|
||||
webFont:
|
||||
Type: 'Aliyun::Serverless::Function'
|
||||
Properties:
|
||||
Handler: dist/src/index.handler
|
||||
Runtime: nodejs10
|
||||
CodeUri: ./
|
||||
Timeout: 60
|
||||
EnvironmentVariables:
|
||||
NODE_PATH: '/mnt/auto/node_modules:/usr/local/lib/node_modules'
|
||||
is_aliyun: 'true'
|
||||
Events:
|
||||
httpTrigger:
|
||||
Type: HTTP
|
||||
Properties:
|
||||
AuthType: ANONYMOUS
|
||||
Methods:
|
||||
- POST
|
||||
- GET
|
||||
24
test/app.e2e-spec.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from './../src/app.module';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('/ (GET)', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Hello World!');
|
||||
});
|
||||
});
|
||||
9
test/jest-e2e.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"moduleFileExtensions": ["js", "json", "ts"],
|
||||
"rootDir": ".",
|
||||
"testEnvironment": "node",
|
||||
"testRegex": ".e2e-spec.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
}
|
||||
}
|
||||
4
tsconfig.build.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
}
|
||||
16
tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es2017",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"esModuleInterop": true,
|
||||
"baseUrl": "./",
|
||||
"incremental": true
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
18
tslint.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": ["tslint:recommended"],
|
||||
"jsRules": {
|
||||
"no-unused-expression": true
|
||||
},
|
||||
"rules": {
|
||||
"quotemark": [true, "single"],
|
||||
"member-access": [false],
|
||||
"ordered-imports": [false],
|
||||
"max-line-length": [true, 150],
|
||||
"member-ordering": [false],
|
||||
"interface-name": [false],
|
||||
"arrow-parens": false,
|
||||
"object-literal-sort-keys": false
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
||||