@qlover/scripts-context 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +3 -3
- package/dist/index.js +41 -10
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var B=Object.create;var b=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var U=Object.getPrototypeOf,M=Object.prototype.hasOwnProperty;var V=(n,e)=>{for(var t in e)b(n,t,{get:e[t],enumerable:!0})},L=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of H(e))!M.call(n,o)&&o!==t&&b(n,o,{get:()=>e[o],enumerable:!(r=q(e,o))||r.enumerable});return n};var z=(n,e,t)=>(t=n!=null?B(U(n)):{},L(e||!n||!n.__esModule?b(t,"default",{value:n,enumerable:!0}):t,n)),Z=n=>L(b({},"__esModule",{value:!0}),n);var
|
|
1
|
+
"use strict";var B=Object.create;var b=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var U=Object.getPrototypeOf,M=Object.prototype.hasOwnProperty;var V=(n,e)=>{for(var t in e)b(n,t,{get:e[t],enumerable:!0})},L=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of H(e))!M.call(n,o)&&o!==t&&b(n,o,{get:()=>e[o],enumerable:!(r=q(e,o))||r.enumerable});return n};var z=(n,e,t)=>(t=n!=null?B(U(n)):{},L(e||!n||!n.__esModule?b(t,"default",{value:n,enumerable:!0}):t,n)),Z=n=>L(b({},"__esModule",{value:!0}),n);var ee={};V(ee,{ColorFormatter:()=>E,ConfigSearch:()=>u,ScriptContext:()=>R,ScriptPlugin:()=>F,Shell:()=>d,defaultFeConfig:()=>I});module.exports=Z(ee);var I={protectedBranches:["master","develop","main"],cleanFiles:["dist","node_modules","yarn.lock","package-lock.json",".eslintcache","*.log"],commitlint:{extends:["@commitlint/config-conventional"]},release:{publishPath:"",autoMergeReleasePR:!1,autoMergeType:"squash",branchName:"release-${pkgName}-${tagName}",PRTitle:"[${pkgName} Release] Branch:${branch}, Tag:${tagName}, Env:${env}",PRBody:`## Publish Details
|
|
2
2
|
|
|
3
3
|
- \u{1F3F7}\uFE0F Version: \${tagName}
|
|
4
4
|
- \u{1F332} Branch: \${branch}
|
|
@@ -14,6 +14,6 @@
|
|
|
14
14
|
- [ ] Please confirm all tests have passed
|
|
15
15
|
- [ ] Please confirm the documentation has been updated
|
|
16
16
|
|
|
17
|
-
> This PR is auto created by release process, please contact the frontend team if there are any questions.`,label:{color:"1A7F37",description:"Release PR",name:"CI-Release"},packagesDirectories:[],changePackagesLabel:"changes:${name}"},envOrder:[".env.local",".env.production",".env"]};var f=z(require("chalk"),1),E=class{constructor(e={fatal:f.default.bgRed.white.bold,error:f.default.red.bold,warn:f.default.yellow.bold,info:f.default.blue,debug:f.default.green,trace:f.default.gray,log:f.default.white}){this.levelColors=e}format(e){let{level:t,args:r}=e,o=this.levelColors[t]||f.default.white;return[typeof o=="function"?o(t.toUpperCase()):t.toUpperCase(),...r]}};var T=require("cosmiconfig"),C=require("lodash-es");function J(n){let e=["json","js","ts","cjs","yaml","yml"];return["package.json",...e.map(t=>`${n}.${t}`),...e.map(t=>`.${n}.${t}`)]}var u=class{name;searchPlaces;_config;loaders;searchCache;constructor(e){let{name:t,searchPlaces:r,defaultConfig:o,loaders:i}=e;if(!t&&!r)throw new Error("searchPlaces or name is required");this.name=t,this.searchPlaces=r||J(t),this._config=o||{},this.loaders=i}get config(){return(0,C.defaultsDeep)({},this.search(),this._config)}getSearchPlaces(){return this.searchPlaces}get(e={}){let{file:t,dir:r=process.cwd()}=e,o={};if(t===!1)return o;let i=(0,T.cosmiconfigSync)(this.name,{searchPlaces:this.searchPlaces,loaders:this.loaders}),s=t?i.load(t):i.search(r);if(s&&typeof s.config=="string")throw new Error(`Invalid configuration file at ${s.filepath}`);return s&&(0,C.isPlainObject)(s.config)?s.config:o}search(){return this.searchCache?this.searchCache:this.searchCache=this.get({})}};var O=require("lodash-es"),A=require("@qlover/fe-corekit");var v=require("lodash-es"),_=require("@qlover/env-loader"),K=[".env.local",".env"];function G(n,e,t){let r=n.env||_.Env.searchEnv({logger:t,preloadList:e.envOrder||K}),o=n.rootPath||process.cwd(),i=n.sourceBranch||r?.get("FE_RELEASE_BRANCH")||r?.get("FE_RELEASE_SOURCE_BRANCH")||"master";return{...n,sourceBranch:i,rootPath:o,env:r}}function Q(n,e,t){let r=n?(0,v.get)(e,n):e,o=typeof r=="object"&&r!==null,i=o?r:{};return o||t.warn(`rootProp ${n} is not an object, it will be overwritten by the default options`),i}function
|
|
17
|
+
> This PR is auto created by release process, please contact the frontend team if there are any questions.`,label:{color:"1A7F37",description:"Release PR",name:"CI-Release"},packagesDirectories:[],changePackagesLabel:"changes:${name}"},envOrder:[".env.local",".env.production",".env"]};var f=z(require("chalk"),1),E=class{constructor(e={fatal:f.default.bgRed.white.bold,error:f.default.red.bold,warn:f.default.yellow.bold,info:f.default.blue,debug:f.default.green,trace:f.default.gray,log:f.default.white}){this.levelColors=e}format(e){let{level:t,args:r}=e,o=this.levelColors[t]||f.default.white;return[typeof o=="function"?o(t.toUpperCase()):t.toUpperCase(),...r]}};var T=require("cosmiconfig"),C=require("lodash-es");function J(n){let e=["json","js","ts","cjs","yaml","yml"];return["package.json",...e.map(t=>`${n}.${t}`),...e.map(t=>`.${n}.${t}`)]}var u=class{name;searchPlaces;_config;loaders;searchCache;constructor(e){let{name:t,searchPlaces:r,defaultConfig:o,loaders:i}=e;if(!t&&!r)throw new Error("searchPlaces or name is required");this.name=t,this.searchPlaces=r||J(t),this._config=o||{},this.loaders=i}get config(){return(0,C.defaultsDeep)({},this.search(),this._config)}getSearchPlaces(){return this.searchPlaces}get(e={}){let{file:t,dir:r=process.cwd()}=e,o={};if(t===!1)return o;let i=(0,T.cosmiconfigSync)(this.name,{searchPlaces:this.searchPlaces,loaders:this.loaders}),s=t?i.load(t):i.search(r);if(s&&typeof s.config=="string")throw new Error(`Invalid configuration file at ${s.filepath}`);return s&&(0,C.isPlainObject)(s.config)?s.config:o}search(){return this.searchCache?this.searchCache:this.searchCache=this.get({})}};var O=require("lodash-es"),A=require("@qlover/fe-corekit");var v=require("lodash-es"),_=require("@qlover/env-loader"),K=[".env.local",".env"];function G(n,e,t){let r=n.env||_.Env.searchEnv({logger:t,preloadList:e.envOrder||K}),o=n.rootPath||process.cwd(),i=n.sourceBranch||r?.get("FE_RELEASE_BRANCH")||r?.get("FE_RELEASE_SOURCE_BRANCH")||"master";return{...n,sourceBranch:i,rootPath:o,env:r}}function Q(n,e,t){let r=n?(0,v.get)(e,n):e,o=typeof r=="object"&&r!==null,i=o?r:{};return o||t.warn(`rootProp ${n} is not an object, it will be overwritten by the default options`),i}function N(n,e,t,r){let o=Q(n,e,r),i=(0,v.merge)({},o,t);return G(i,e,r)}var D=require("child_process");var k=require("lodash-es");var y=class{constructor(e,t,r,o){this.level=e;this.args=t;this.loggerName=r;this.context=o;this.timestamp=Date.now()}timestamp};var h=class{constructor(e){this.value=e}};var W={fatal:0,error:10,warn:20,info:30,debug:40,trace:50,log:60},P=class{constructor(e={}){this.options=e;e.name=e.name||Date.now().toString(),e.levels=e.levels||W,e.handlers=Array.isArray(e.handlers)?e.handlers:e.handlers?[e.handlers]:[]}addAppender(e){this.options.handlers.push(e)}context(e){return new h(e)}print(e,t){let{levels:r,level:o,silent:i,handlers:s}=this.options;if(i)return;let a=t.slice(-1)[0],c=t.length>1&&a instanceof h;if(a=c?a:void 0,t=c?t.slice(0,-1):t,e=a?.value?.level??e,o&&r){let p=r[o],m=r[e];if(p!=null&&m!=null&&m>p)return}let l=new y(e,t,this.options.name,a),g=Array.isArray(s)?s:[s];for(let p of g)p&&p.append(l)}log(...e){this.print("info",e)}fatal(...e){this.print("fatal",e)}error(...e){this.print("error",e)}warn(...e){this.print("warn",e)}info(...e){this.print("info",e)}debug(...e){this.print("debug",e)}trace(...e){this.print("trace",e)}};var w=class{constructor(e=null){this.formatter=e}setFormatter(e){this.formatter=e}append(e){let{level:t,args:r,loggerName:o}=e,i=this.formatter?this.formatter.format(e):[o?`[${o}]`:null,...r].filter(s=>s!=null);(console[t]||console.log)(...Array.isArray(i)?i:[i])}};var X={hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1,timeZone:"UTC"},Y="[{loggerName}] [{formattedTimestamp} {level}]",S=class{constructor(e={}){this.options=e}updateOptions(e){this.options={...this.options,...e}}replacePrefix(e,t){return e.replace(/\{([^{}]+)\}/g,(r,o)=>o in t?String(t[o]??""):"")}format({timestamp:e,level:t,args:r,context:o,loggerName:i}){let{locale:s="zh-CN",localeOptions:a,prefixTemplate:c=Y}=this.options,l=o?.formatType??"datetime",g=l==="date"?"toLocaleDateString":l==="time"?"toLocaleTimeString":"toLocaleString",p=new Date(e)[g](s,{...X,...a});return[this.replacePrefix(c,{...o,timestamp:e.toString(),level:t,loggerName:i,formattedTimestamp:p,locale:s}),...r]}};var $=require("lodash-es"),d=class n{constructor(e,t=new Map){this.config=e;this.cache=t}get logger(){return this.config.logger}static format(e="",t={}){return(0,$.template)(e)(t)}format(e="",t={}){try{return n.format(e,t)}catch(r){throw this.logger.error(`Unable to render template with context:
|
|
18
18
|
${e}
|
|
19
|
-
${JSON.stringify(t)}`),this.logger.error(r),r}}exec(e,t={}){let{context:r,...o}=t;return typeof e=="string"?this.execFormattedCommand(this.format(e,r||{}),o):this.execFormattedCommand(e,o)}run(e,t={}){return this.exec(e,{silent:!0,...t})}async execFormattedCommand(e,t={}){let r=this.config.execPromise;if(!r)throw new Error("execPromise is not defined");let{dryRunResult:o,silent:i,dryRun:s,isCache:a}=t,c=s!==void 0?s:this.config.dryRun,l=a!==void 0?!!a:!!this.config.isCache,g=typeof e=="string"?e:e.join(" "),p=l&&this.cache.has(g);if(i||this.logger.debug(e),c)return Promise.resolve(o);if(p)return this.cache.get(g);let m=r(e,t);return this.cache.has(g)||this.cache.set(g,m),m}};var x={exec(n,e){let t=Array.isArray(n)?n.join(" "):n;return new Promise((r,o)=>{(0
|
|
19
|
+
${JSON.stringify(t)}`),this.logger.error(r),r}}exec(e,t={}){let{context:r,...o}=t;return typeof e=="string"?this.execFormattedCommand(this.format(e,r||{}),o):this.execFormattedCommand(e,o)}run(e,t={}){return this.exec(e,{silent:!0,...t})}async execFormattedCommand(e,t={}){let r=this.config.execPromise;if(!r)throw new Error("execPromise is not defined");let{dryRunResult:o,silent:i,dryRun:s,isCache:a}=t,c=s!==void 0?s:this.config.dryRun,l=a!==void 0?!!a:!!this.config.isCache,g=typeof e=="string"?e:e.join(" "),p=l&&this.cache.has(g);if(i||this.logger.debug(e),c)return Promise.resolve(o);if(p)return this.cache.get(g);let m=r(e,t);return this.cache.has(g)||this.cache.set(g,m),m}};var x={exec(n,e){let t=Array.isArray(n)?n.join(" "):n;return new Promise((r,o)=>{(0,D.exec)(t,{encoding:"utf-8",...e},(i,s,a)=>{let c;i?i.code===void 0?c=1:c=i.code:c=0,c===0?r(s.trim()):o(new Error(a||s))})})},getConfig(n){return new u({name:"fe-config",defaultConfig:(0,k.merge)({},I,n)})},logger(n,e){return new P({level:e?"debug":"info",name:n,handlers:new w(new S({prefixTemplate:"[{loggerName} {formattedTimestamp} {level}]",localeOptions:{timeZone:"Asia/Shanghai"}}))})},options(n,e){let{logger:t,shell:r,feConfig:o,dryRun:i,verbose:s,options:a}=e,c=a||{},l=t||x.logger(n,!!s),g=r||new d({logger:l,dryRun:i,execPromise:c.execPromise||x.exec}),p=(0,k.merge)({},o,{[n]:e.options});return{options:c,dryRun:!!i,shell:g,logger:l,verbose:!!s,feConfig:x.getConfig(p).config}}};var R=class extends A.ExecutorContextImpl{constructor(t,r={}){if(!t||typeof t!="string")throw new Error("ScriptContext name is required");let{feConfig:o,logger:i,shell:s,dryRun:a,verbose:c,options:l}=x.options(t,r);super(l);this.name=t;this.feConfig=o,this.logger=i,this.shell=s,this.dryRun=a,this.verbose=c,this.setOptions(N(t,o,l,i))}logger;shell;feConfig;dryRun;verbose;get options(){return this.parameters}get env(){if(!this.options.env)throw new Error("Environment is not initialized");return this.options.env}setOptions(t){this.setParameters((0,O.merge)(this.options,t))}getEnv(t,r){return this.env.get(t)??r}getOptions(t,r){return t?(0,O.get)(this.options,t,r):this.options}};var j=require("lodash-es"),F=class{constructor(e,t,r={}){this.context=e;this.pluginName=t;this.props=r;this.setConfig(this.getInitialProps(r))}onlyOne=!0;getInitialProps(e){if(typeof this.context.options!="object")return{};let t=this.context.options[this.pluginName],r=this.context.getOptions(this.pluginName),o=t||r;return e?(0,j.merge)({},o,e):o||{}}get logger(){return this.context.logger}get shell(){return this.context.shell}get options(){return this.context.getOptions(this.pluginName,{})}enabled(e,t){let r=this.getConfig("skip");return!(r===!0||typeof r=="string"&&e===r)}getConfig(e,t){return e?this.context.getOptions([this.pluginName,...Array.isArray(e)?e:[e]],t):this.context.getOptions(this.pluginName,t)}setConfig(e){this.context.setOptions({[this.pluginName]:e})}onBefore(e){}onExec(e){}onSuccess(e){}onError(e){}onFinally(e){}async step(e){this.logger.log(),this.logger.info(e.label),this.logger.log();try{let t=await e.task();return this.logger.info(`${e.label} - success`),t}catch(t){throw this.logger.error(t),t}}};0&&(module.exports={ColorFormatter,ConfigSearch,ScriptContext,ScriptPlugin,Shell,defaultFeConfig});
|
package/dist/index.js
CHANGED
|
@@ -962,7 +962,7 @@ var ConsoleHandler = class {
|
|
|
962
962
|
* @example Without formatter
|
|
963
963
|
* ```typescript
|
|
964
964
|
* const handler = new ConsoleHandler();
|
|
965
|
-
* // Output:
|
|
965
|
+
* // Output: [loggerName] + raw message (logger name omitted if empty)
|
|
966
966
|
* ```
|
|
967
967
|
*
|
|
968
968
|
* @example With timestamp formatter
|
|
@@ -1068,7 +1068,7 @@ var ConsoleHandler = class {
|
|
|
1068
1068
|
* timestamp: Date.now(),
|
|
1069
1069
|
* loggerName: 'app'
|
|
1070
1070
|
* });
|
|
1071
|
-
* // Console output: Application started
|
|
1071
|
+
* // Console output: [app] Application started
|
|
1072
1072
|
*
|
|
1073
1073
|
* // Message with context
|
|
1074
1074
|
* handler.append({
|
|
@@ -1080,7 +1080,7 @@ var ConsoleHandler = class {
|
|
|
1080
1080
|
* timestamp: Date.now(),
|
|
1081
1081
|
* loggerName: 'db'
|
|
1082
1082
|
* });
|
|
1083
|
-
* // Console output: Database connection failed { host: 'localhost', ... }
|
|
1083
|
+
* // Console output: [db] Database connection failed { host: 'localhost', ... }
|
|
1084
1084
|
* ```
|
|
1085
1085
|
*
|
|
1086
1086
|
* @example Formatted event handling
|
|
@@ -1138,8 +1138,10 @@ var ConsoleHandler = class {
|
|
|
1138
1138
|
* ```
|
|
1139
1139
|
*/
|
|
1140
1140
|
append(event) {
|
|
1141
|
-
const { level, args } = event;
|
|
1142
|
-
const formattedArgs = this.formatter ? this.formatter.format(event) : args
|
|
1141
|
+
const { level, args, loggerName } = event;
|
|
1142
|
+
const formattedArgs = this.formatter ? this.formatter.format(event) : [loggerName ? `[${loggerName}]` : null, ...args].filter(
|
|
1143
|
+
(x) => x != null
|
|
1144
|
+
);
|
|
1143
1145
|
(console[level] || console.log)(...Array.isArray(formattedArgs) ? formattedArgs : [formattedArgs]);
|
|
1144
1146
|
}
|
|
1145
1147
|
};
|
|
@@ -1152,6 +1154,7 @@ var defaultLocaleOptions = {
|
|
|
1152
1154
|
hour12: false,
|
|
1153
1155
|
timeZone: "UTC"
|
|
1154
1156
|
};
|
|
1157
|
+
var defaultPrefixTemplate = "[{loggerName}] [{formattedTimestamp} {level}]";
|
|
1155
1158
|
var TimestampFormatter = class {
|
|
1156
1159
|
/**
|
|
1157
1160
|
* Creates a new TimestampFormatter instance
|
|
@@ -1169,15 +1172,40 @@ var TimestampFormatter = class {
|
|
|
1169
1172
|
constructor(options = {}) {
|
|
1170
1173
|
this.options = options;
|
|
1171
1174
|
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Updates formatter options at runtime.
|
|
1177
|
+
* Merges the given partial options into the current options; subsequent formatting uses the updated values.
|
|
1178
|
+
*
|
|
1179
|
+
* @param partial - Partial options to merge (e.g. locale, prefixTemplate, localeOptions)
|
|
1180
|
+
* @since 1.1.0
|
|
1181
|
+
*
|
|
1182
|
+
* @example Update locale at runtime
|
|
1183
|
+
* ```typescript
|
|
1184
|
+
* const formatter = new TimestampFormatter({ locale: 'zh-CN' });
|
|
1185
|
+
* // later: switch to English
|
|
1186
|
+
* formatter.updateOptions({ locale: 'en-US' });
|
|
1187
|
+
* ```
|
|
1188
|
+
*
|
|
1189
|
+
* @example Update multiple options
|
|
1190
|
+
* ```typescript
|
|
1191
|
+
* formatter.updateOptions({
|
|
1192
|
+
* locale: 'ja-JP',
|
|
1193
|
+
* localeOptions: { timeZone: 'Asia/Tokyo', hour12: false }
|
|
1194
|
+
* });
|
|
1195
|
+
* ```
|
|
1196
|
+
*/
|
|
1197
|
+
updateOptions(partial) {
|
|
1198
|
+
this.options = { ...this.options, ...partial };
|
|
1199
|
+
}
|
|
1172
1200
|
/**
|
|
1173
1201
|
* Replaces template variables in the prefix string with actual values
|
|
1174
1202
|
*
|
|
1175
|
-
*
|
|
1176
|
-
*
|
|
1203
|
+
* Placeholders like {timestamp} are replaced with values from vars.
|
|
1204
|
+
* Unknown or missing variables are skipped (replaced with empty string), not left as literal.
|
|
1177
1205
|
*
|
|
1178
1206
|
* @param template - The template string containing variables in curly braces
|
|
1179
1207
|
* @param vars - Object containing variable names and their values
|
|
1180
|
-
* @returns The template string with
|
|
1208
|
+
* @returns The template string with known variables replaced, unknowns removed
|
|
1181
1209
|
*
|
|
1182
1210
|
* @example Internal usage
|
|
1183
1211
|
* ```typescript
|
|
@@ -1195,7 +1223,10 @@ var TimestampFormatter = class {
|
|
|
1195
1223
|
* @protected
|
|
1196
1224
|
*/
|
|
1197
1225
|
replacePrefix(template, vars) {
|
|
1198
|
-
return template.replace(
|
|
1226
|
+
return template.replace(
|
|
1227
|
+
/\{([^{}]+)\}/g,
|
|
1228
|
+
(_match, p1) => p1 in vars ? String(vars[p1] ?? "") : ""
|
|
1229
|
+
);
|
|
1199
1230
|
}
|
|
1200
1231
|
/**
|
|
1201
1232
|
* Formats a log event by adding a timestamp prefix
|
|
@@ -1245,7 +1276,7 @@ var TimestampFormatter = class {
|
|
|
1245
1276
|
const {
|
|
1246
1277
|
locale = "zh-CN",
|
|
1247
1278
|
localeOptions,
|
|
1248
|
-
prefixTemplate =
|
|
1279
|
+
prefixTemplate = defaultPrefixTemplate
|
|
1249
1280
|
} = this.options;
|
|
1250
1281
|
const formatType = context?.formatType ?? "datetime";
|
|
1251
1282
|
const dateFormatMethod = formatType === "date" ? "toLocaleDateString" : formatType === "time" ? "toLocaleTimeString" : "toLocaleString";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qlover/scripts-context",
|
|
3
3
|
"description": "A scripts context for frontwork",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.1.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"homepage": "https://github.com/qlover/fe-base/tree/master/packages/scripts-context#readme",
|
|
@@ -43,14 +43,14 @@
|
|
|
43
43
|
"@commitlint/types": "^19.8.0",
|
|
44
44
|
"@types/lodash-es": "^4.17.12",
|
|
45
45
|
"@types/shelljs": "^0.8.15",
|
|
46
|
-
"@qlover/logger": "1.
|
|
46
|
+
"@qlover/logger": "1.1.1"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"chalk": "^5.3.0",
|
|
50
50
|
"cosmiconfig": "^9.0.0",
|
|
51
51
|
"lodash-es": "^4.17.22",
|
|
52
|
-
"@qlover/env-loader": "1.0.
|
|
53
|
-
"@qlover/fe-corekit": "3.
|
|
52
|
+
"@qlover/env-loader": "1.0.2",
|
|
53
|
+
"@qlover/fe-corekit": "3.2.1"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"lint": "eslint src --fix",
|