@moneko/core 3.1.0-beta.2 → 3.1.0-beta.3

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/lib/dev.js CHANGED
@@ -1,6 +1,90 @@
1
- import e from"@soda/friendly-errors-webpack-plugin";import o from"chalk";import r from"webpack";import{merge as t}from"webpack-merge";import{clientConfig as s}from"./common.js";import{CONFIG as p}from"./config.js";import{hasPkg as i}from"./has-pkg.js";import{getIPv4 as a,getPort as m}from"./net.js";import{isReact as n}from"./process-env.js";import{isFunction as l,resolveProgramPath as d}from"./utils.js";let{HotModuleReplacementPlugin:c}=r,{yellow:v,green:f}=o,h=a(),w=p.devServer.port||3e3,u=await m(w,65535,p.devServer.host),g=p.devServer.port!==u,k=i("@moneko/mock"),$=k&&(await import("@moneko/mock")).default,S=n&&(await import("@pmmmwh/react-refresh-webpack-plugin")).default,b=!1===p.devtool||p.devtool?p.devtool:"eval-cheap-module-source-map";p.devServer.port=u;let y="/"===p.basename,j=p.devServer.https?"https:":"http:",x=y?"":p.basename,A=t(s,{devtool:b,mode:"development",devServer:{headers:{"Access-Control-Allow-Origin":"*"},compress:p.devServer.compress,host:"0.0.0.0",port:u,historyApiFallback:y||{index:x.endsWith("/")?x:`${x}/`,disableDotRule:!0},https:p.devServer.https,proxy:p.proxy,allowedHosts:p.devServer.allowedHosts,client:{// 在浏览器中以百分比显示编译进度。
2
- progress:!1,logging:"info",// 当出现编译错误或警告时,在浏览器中显示全屏覆盖。
3
- overlay:!1},static:{watch:{ignored:e=>e.endsWith(".d.ts")||/\/node_modules\//.test(e)}},setupMiddlewares:(e,o)=>{if(!o)throw Error("webpack-dev-server is not defined");return o.app&&l($)&&$(o.app,d("mock/")),e},open:!1,hot:!0},plugins:[new c,S&&new S,new e({compilationSuccessInfo:{messages:[`You application is running here:
1
+ import FriendlyErrorsWebpackPlugin from '@soda/friendly-errors-webpack-plugin';
2
+ import chalk from 'chalk';
3
+ import webpack from 'webpack';
4
+ import { merge } from 'webpack-merge';
5
+ import { clientConfig } from './common.js';
6
+ import { CONFIG } from './config.js';
7
+ import { hasPkg } from './has-pkg.js';
8
+ import { getIPv4, getPort } from './net.js';
9
+ import { isReact } from './process-env.js';
10
+ import { isFunction, resolveProgramPath } from './utils.js';
11
+ const { HotModuleReplacementPlugin } = webpack;
12
+ const { yellow, green } = chalk;
13
+ const ip = getIPv4();
14
+ const oldPord = CONFIG.devServer.port || 3000;
15
+ const port = await getPort(oldPord, 65535, CONFIG.devServer.host);
16
+ const skipPort = CONFIG.devServer.port !== port;
17
+ const hasMockMiddlewares = hasPkg('@moneko/mock');
18
+ const mockMiddlewares = hasMockMiddlewares && (await import('@moneko/mock')).default;
19
+ const ReactRefresh = isReact && (await import('@pmmmwh/react-refresh-webpack-plugin')).default;
20
+ const devtool = CONFIG.devtool === false || CONFIG.devtool ? CONFIG.devtool : 'eval-cheap-module-source-map';
21
+ CONFIG.devServer.port = port;
22
+ const initRouteBase = CONFIG.basename === '/';
23
+ const protocol = CONFIG.devServer.https ? 'https:' : 'http:';
24
+ const routeBase = initRouteBase ? '' : CONFIG.basename;
25
+ const client = merge(clientConfig, {
26
+ devtool: devtool,
27
+ mode: 'development',
28
+ devServer: {
29
+ headers: {
30
+ 'Access-Control-Allow-Origin': '*'
31
+ },
32
+ compress: CONFIG.devServer.compress,
33
+ host: '0.0.0.0',
34
+ port: port,
35
+ historyApiFallback: initRouteBase || {
36
+ index: routeBase.endsWith('/') ? routeBase : `${routeBase}/`,
37
+ disableDotRule: true
38
+ },
39
+ https: CONFIG.devServer.https,
40
+ proxy: CONFIG.proxy,
41
+ allowedHosts: CONFIG.devServer.allowedHosts,
42
+ client: {
43
+ // 在浏览器中以百分比显示编译进度。
44
+ progress: false,
45
+ logging: 'info',
46
+ // 当出现编译错误或警告时,在浏览器中显示全屏覆盖。
47
+ overlay: false
48
+ },
49
+ static: {
50
+ watch: {
51
+ ignored: (f)=>{
52
+ // 生成的类型定义不要监听,否则会引发全局的 reload 使 HMR 失去意义
53
+ return f.endsWith('.d.ts') || /\/node_modules\//.test(f);
54
+ }
55
+ }
56
+ },
57
+ setupMiddlewares: (middlewares, devServer)=>{
58
+ if (!devServer) {
59
+ throw new Error('webpack-dev-server is not defined');
60
+ }
61
+ if (devServer.app && isFunction(mockMiddlewares)) {
62
+ mockMiddlewares(devServer.app, resolveProgramPath('mock/'));
63
+ }
64
+ return middlewares;
65
+ },
66
+ open: false,
67
+ hot: true
68
+ },
69
+ plugins: [
70
+ new HotModuleReplacementPlugin(),
71
+ ReactRefresh && new ReactRefresh(),
72
+ new FriendlyErrorsWebpackPlugin({
73
+ compilationSuccessInfo: {
74
+ messages: [
75
+ `You application is running here:
4
76
 
5
- Local: ${o.cyan(`${j}//${p.devServer.host}:${u}${x}`)}
6
- Network: ${o.cyan(`${j}//${h}:${u}${x}`)}`],notes:g?[`Port ${v(w)} is in use, trying ${f(u)} instead`]:[]},clearConsole:!0})].filter(Boolean)});export default[A];
77
+ Local: ${chalk.cyan(`${protocol}//${CONFIG.devServer.host}:${port}${routeBase}`)}
78
+ Network: ${chalk.cyan(`${protocol}//${ip}:${port}${routeBase}`)}`
79
+ ],
80
+ notes: skipPort ? [
81
+ `Port ${yellow(oldPord)} is in use, trying ${green(port)} instead`
82
+ ] : []
83
+ },
84
+ clearConsole: true
85
+ })
86
+ ].filter(Boolean)
87
+ });
88
+ export default [
89
+ client
90
+ ];
package/lib/docs.js CHANGED
@@ -1,2 +1,107 @@
1
- import{statSync as e}from"fs";import{dirname as t,join as r}from"path";import{watch as o}from"chokidar";import{CONFIG as n}from"./config.js";import i from"./generate-api.js";import s from"./object-listener.js";import{FRAMEWORK as p,FRAMEWORKNAME as l,createElement as a,isLibrary as c,isReact as d,isSolid as m}from"./process-env.js";import{resolveProgramPath as f}from"./utils.js";let u="@app/comment";export const docs=s({});let $={[u]:{}},g=`() => ${a}(SuspenseComp, { comp: $1 })`,b=`import { ${a}${m?",Dynamic":""} } from "${l}${m?"/web":""}";import SuspenseComp from "@app/suspense";`;// 要执行的函数
2
- function h(e,o){let s=e.replace(RegExp(`^${n.alias["@pkg"]}`),""),p=t(s).replace(/^\//,""),l=[u,p].join("/"),c=s.split("/").pop()?.replace(/\.tsx?/,".md");if(!c)return;$[u][p]||($[u][p]={});let f=r(l,c);if("deleted"===o)$[u][p][c]&&delete $[u][p][c];else{let t=i(e);$[f]=t,t?$[u][p][c]=`rr(() => import(/* webpackChunkName: '${f}' */'${f}?raw').then((res) => ({default: ${d?"() =>":""}${a}(${m?"Dynamic":"'n-md'"}, {text: res.default, ${m?"component: 'n-md', ":""}css: 'table td a {display:inline-flex;align-items:center;gap:2px;}table td a n-img{display:inline-block;overflow:hidden;border-radius:var(--border-radius);inline-size:18px;block-size:18px;}'})})))rr`:$[u][p][c]&&delete $[u][p][c]}let h={};for(let e in $[u])Object.prototype.hasOwnProperty.call($[u],e)&&(h[e]=Object.values($[u][e]));let x={...$};delete x[u],Object.assign(docs.data,{...x,"@app/docs":`${b}export default ${JSON.stringify(h).replace(/"rr\((.+?)\)rr"/g,g)}`})}["react","solid"].includes(p)||(g="$1",b=""),c&&function(){let t=[],r=o(f("components"),{ignored:[/(^|[\\/\\])\../,/(^|[\\/\\])__tests__([\\/\\]|$)/],persistent:!0,ignoreInitial:!1});function n(t){return/\.tsx?$/.test(t)&&e(t).isFile()}r.on("add",e=>{n(e)&&(h(e,"added"),t.push(e))}),r.on("change",e=>{n(e)&&h(e,"change")}),r.on("unlink",e=>{n(e)&&(h(e,"deleted"),t.splice(t.indexOf(e),1))}),r.on("ready",()=>{t.forEach(e=>{h(e,"change")})}),process.on("SIGINT",function(){r.close()})}();
1
+ import { statSync } from 'fs';
2
+ import { dirname, join } from 'path';
3
+ import { watch } from 'chokidar';
4
+ import { CONFIG } from './config.js';
5
+ import generateApi from './generate-api.js';
6
+ import objectListener from './object-listener.js';
7
+ import { FRAMEWORK, FRAMEWORKNAME, createElement, isLibrary, isReact, isSolid } from './process-env.js';
8
+ import { resolveProgramPath } from './utils.js';
9
+ const base = '@app/comment';
10
+ const apiEntry = '@app/docs';
11
+ export const docs = objectListener({});
12
+ const cacheEnvApi = {
13
+ [base]: {}
14
+ };
15
+ let replaceStr = `() => ${createElement}(SuspenseComp, { comp: $1 })`;
16
+ let prefixStr = `import { ${createElement}${isSolid ? ',Dynamic' : ''} } from "${FRAMEWORKNAME}${isSolid ? '/web' : ''}";import SuspenseComp from "@app/suspense";`;
17
+ if (![
18
+ 'react',
19
+ 'solid'
20
+ ].includes(FRAMEWORK)) {
21
+ replaceStr = '$1';
22
+ prefixStr = '';
23
+ }
24
+ // 要执行的函数
25
+ function handleFileChange(filePath, changeType) {
26
+ const fil = filePath.replace(new RegExp(`^${CONFIG.alias['@pkg']}`), '');
27
+ const __dir = dirname(fil).replace(/^\//, '');
28
+ const apiDir = [
29
+ base,
30
+ __dir
31
+ ].join('/');
32
+ const name = fil.split('/').pop()?.replace(/\.tsx?/, '.md');
33
+ if (!name) return;
34
+ if (!cacheEnvApi[base][__dir]) {
35
+ cacheEnvApi[base][__dir] = {};
36
+ }
37
+ const target = join(apiDir, name);
38
+ if (changeType === 'deleted') {
39
+ if (cacheEnvApi[base][__dir][name]) {
40
+ delete cacheEnvApi[base][__dir][name];
41
+ }
42
+ } else {
43
+ const api = generateApi(filePath);
44
+ cacheEnvApi[target] = api;
45
+ if (api) {
46
+ cacheEnvApi[base][__dir][name] = `rr(() => import(/* webpackChunkName: '${target}' */'${target}?raw').then((res) => ({default: ${isReact ? '() =>' : ''}${createElement}(${isSolid ? 'Dynamic' : "'n-md'"}, {text: res.default, ${isSolid ? "component: 'n-md', " : ''}css: 'table td a {display:inline-flex;align-items:center;gap:2px;}table td a n-img{display:inline-block;overflow:hidden;border-radius:var(--border-radius);inline-size:18px;block-size:18px;}'})})))rr`;
47
+ } else if (cacheEnvApi[base][__dir][name]) {
48
+ delete cacheEnvApi[base][__dir][name];
49
+ }
50
+ }
51
+ const basestr = {};
52
+ for(const k in cacheEnvApi[base]){
53
+ if (Object.prototype.hasOwnProperty.call(cacheEnvApi[base], k)) {
54
+ basestr[k] = Object.values(cacheEnvApi[base][k]);
55
+ }
56
+ }
57
+ const env = {
58
+ ...cacheEnvApi
59
+ };
60
+ delete env[base];
61
+ Object.assign(docs.data, {
62
+ ...env,
63
+ [apiEntry]: `${prefixStr}export default ${JSON.stringify(basestr).replace(/"rr\((.+?)\)rr"/g, replaceStr)}`
64
+ });
65
+ }
66
+ function watchDirectory() {
67
+ const files = [];
68
+ const watcher = watch(resolveProgramPath('components'), {
69
+ ignored: [
70
+ /(^|[\\/\\])\../,
71
+ /(^|[\\/\\])__tests__([\\/\\]|$)/
72
+ ],
73
+ persistent: true,
74
+ ignoreInitial: false
75
+ });
76
+ function isTs(path) {
77
+ return /\.tsx?$/.test(path) && statSync(path).isFile();
78
+ }
79
+ watcher.on('add', (path)=>{
80
+ if (isTs(path)) {
81
+ handleFileChange(path, 'added');
82
+ files.push(path);
83
+ }
84
+ });
85
+ watcher.on('change', (path)=>{
86
+ if (isTs(path)) {
87
+ handleFileChange(path, 'change');
88
+ }
89
+ });
90
+ watcher.on('unlink', (path)=>{
91
+ if (isTs(path)) {
92
+ handleFileChange(path, 'deleted');
93
+ files.splice(files.indexOf(path), 1);
94
+ }
95
+ });
96
+ watcher.on('ready', ()=>{
97
+ files.forEach((f)=>{
98
+ handleFileChange(f, 'change');
99
+ });
100
+ });
101
+ process.on('SIGINT', function() {
102
+ watcher.close();
103
+ });
104
+ }
105
+ if (isLibrary) {
106
+ watchDirectory();
107
+ }
package/lib/done.js CHANGED
@@ -1 +1,11 @@
1
- export default class{constructor(o){this.options=Object.assign({},o)}apply(o){o.hooks.done.tap("DoneWebpackPlugin",()=>{this.options.done?.()})}}
1
+ class DoneWebpackPlugin {
2
+ constructor(options){
3
+ this.options = Object.assign({}, options);
4
+ }
5
+ apply(compiler) {
6
+ compiler.hooks.done.tap('DoneWebpackPlugin', ()=>{
7
+ this.options.done?.();
8
+ });
9
+ }
10
+ }
11
+ export default DoneWebpackPlugin;
package/lib/esm.js CHANGED
@@ -1 +1,7 @@
1
- export default function t(t,...e){let r=t.raw[0];for(let a=0;a<e.length;a++)r+=e[a]+t.raw[a+1];return`data:text/javascript;base64,${Buffer.from(r).toString("base64")}`}
1
+ export default function esm(templateStrings, ...substitutions) {
2
+ let js = templateStrings.raw[0];
3
+ for(let i = 0; i < substitutions.length; i++){
4
+ js += substitutions[i] + templateStrings.raw[i + 1];
5
+ }
6
+ return `data:text/javascript;base64,${Buffer.from(js).toString('base64')}`;
7
+ }
package/lib/fallback.js CHANGED
@@ -1 +1,6 @@
1
- import{CONFIG as l}from"./config.js";let a="export default null";l.fallbackCompPath&&(a=`import Fallback from "${l.fallbackCompPath}";export default Fallback;`);export default a;
1
+ import { CONFIG } from './config.js';
2
+ let fallback = 'export default null';
3
+ if (CONFIG.fallbackCompPath) {
4
+ fallback = `import Fallback from "${CONFIG.fallbackCompPath}";export default Fallback;`;
5
+ }
6
+ export default fallback;
@@ -1,3 +1,341 @@
1
- import{readFileSync as e}from"fs";import{dirname as t}from"path";import n from"typescript";import{CONFIG as r}from"./config.js";let{ScriptKind:l,ScriptTarget:o,SyntaxKind:i,createSourceFile:a,forEachChild:c,getLeadingCommentRanges:u,isInterfaceDeclaration:s,isQuestionToken:f,isPropertySignature:p,isFunctionTypeNode:m,isUnionTypeNode:g,isMethodSignature:$}=n,h={};function b(e){let t=u(e.getSourceFile().text,e.pos);if(t){let n=e.getSourceFile().text.substring(t[0].pos,t[0].end),r=n.match(/\/\*\*([\s\S]*?)\*\//);if(r){let e=r[1].replace(/^\s*\* ?/gm,"").replace(/\s+$/,"").trim();return e}}return null}function d(e){if(!e)return null;let t=e.match(/@since\s+([^\n]+)/);return t?t[1].trim():null}function y(e){if(!e)return null;let t=e.match(/@author (\w+)\s*(?:<([^>]+)>)?/);if(t?.length){let e=t[1].trim(),n=t[2]?.trim(),r=/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(n);return n?r&&(n=`mailto:${n}`):n=`https://github.com/${e}`,`[![${e}](https://avatars.githubusercontent.com/${e}?s=64)${e}](${n})`}return null}function x(e){if(!e)return null;let t=e.match(/@ignore\s+([^\n]+)/);return t?t[1].trim():null}let j=/(?<!['"])(unknown|any|void|bigint|object|undefined|null|boolean|number|string|symbol)(?!['"])/g;function T(e,t){if(!e)return null;let n=e.replace(/\b([A-Z][a-zA-Z0-9]*)\b/g,e=>{if(h[e]){let n=`/${[r.basename,h[e]].join("/").split("/").filter(Boolean).join("/")}`;return t?`[\\color{#009688}{${e}}](${n})`:`[${e}](${n})`}return t?`\\color{#009688}{${e}}`:e});return t&&(n=n.replace(j,e=>`\\color{#009688}{${e}}`)),n}function B(e){return(function(e){let t;if(!e)return null;let n=[];for(;t=/\\color{([^|}]*)\|?([^|}]*)\|?([^|}]*)\|?([^}]*)}{([^}]*)}/g.exec(e);)n.push(t[0]);return e.replace(/(\{|\}|\[|\]|\(|\)|=>|keyof|typeof|true|false)/g,e=>n.some(t=>t.includes(e))?e:`\\color{#569cd6}{${e}}`)})(e)?.replace(/^\s*\|\s*|\s*\|\s*$/gm,"").replace(/\n/g,"<br/>").replace(/\*/g,"\\*").replace(/\|/g,"\\|").replace(/(['"])((?:(?!\1).)*)\1/g,"\\color{#ce9178}{$1$2$1}")}function k(e){return e?e.replace(/^@[a-z].+/gm,"").replace(/(\n\s+)+/g,"<br />").replace(/\n/g,"<br />").replace(/(<br \/>)$/g,""):null}export default function w(u){let j=e(u,"utf-8"),w=a(u,j,o.Latest,!0,l.TS),E="";return c(w,e=>{s(e)?E+=function(e){let l=e.name.text;Object.assign(h,{[e.name.text]:t(e.getSourceFile().fileName).replace(r.alias["@pkg"],"")});let o=b(e),a=d(o),c=k(o),u=x(o)?.split("|")||[],s=u.includes("comment"),j=u.includes("initial"),w=u.includes("optional"),E=u.includes("version"),S=u.includes("author"),v=c?`\\color{|4||0.45}{${l}}`:l,F=(function(e){let t=[];if(e.heritageClauses){for(let r of e.heritageClauses)if(r.token===n.SyntaxKind.ExtendsKeyword)for(let e of r.types)t.push(e.getText())}return t})(e)?.map(e=>`<n-tag color="#4c81db" css=".tag{gap:0px;}">${T(B(e))}</n-tag>`),z=[c,v,a&&`\\color{#52c11b|1||0.9}{${a}}`,F.length>0&&`<sub>\`extends\`</sub> ${F.join(" ")}`].filter(Boolean).join(" "),K=`## ${z}`,N=e.members.filter(// 排除 never
2
- e=>p(e)&&e.type?.kind!==i.NeverKeyword||$(e)),q=[];if(N.length){N.forEach(e=>{let t=T(B($(e)?`(${e.parameters.map(e=>`${e.name.getText()}: ${e.type?.getText()||"any"}`).join(", ")}): ${e.type?.getText()||"any"}`:e.type?.getText()||"any"),!0),n=b(e),r=B(e.name.getText());!r||r.startsWith("\\color")||($(e)||function(e){if(e.type){if(!g(e.type))return m(e.type);for(let t of e.type.types)if(m(t))return!0}return!1}(e)||t?.includes("=>")?r=`\\color{#f9a913}{${r}}`:/^("|')(.+)("|')$/.test(r)||(r=`\\color{#4c81db}{${r}}`));let l=e.questionToken&&f(e.questionToken);q.push([r,!w&&`\\color{${l?"#f9a913":"#52c11b"}\\|\\|\\|0.9}{${l?"✘":"✔"}}`,!s&&B(k(n)),t,!j&&B(function(e){if(!e)return null;let t=e.match(/@default\s+([^\n]+)/);return t?t[1].trim():null}(n)),!E&&B(d(n)),!S&&(y(n)||y(o))])});let e=!1,t=!1,n=!1,r=!1;q.forEach(l=>{!s&&l[2]&&(r=!0),!j&&l[4]&&(n=!0),!E&&l[5]&&(t=!0),!S&&l[6]&&(e=!0)}),K+="\n";let l=["属性",!w&&"必要",r&&"说明","类型",n&&"默认值",t&&"版本",e&&"作者"].filter(Boolean).join("|");K+=`|${l}|`;let i=[":-",!w&&":-",r&&":-",":-",n&&":-",t&&":-",e&&":-"].filter(Boolean);K+="\n";let a=i.join("|");K+=`|${a}|`,q.forEach(l=>{K+="\n";let o=[l[0]||"-",!w&&(l[1]||"-"),r&&(l[2]||"-"),l[3]||"-",n&&(l[4]||"-"),t&&(l[5]||"-"),e&&(l[6]||"-")].filter(Boolean).join("|");K+=`|${o}|`}),K+="\n"}return K+="\n"}(e):n.isEnumDeclaration(e)&&(E+=function(e){let l=e.name.text,o=b(e),i=k(o),a=d(o);Object.assign(h,{[e.name.text]:t(e.getSourceFile().fileName).replace(r.alias["@pkg"],"")});let c=i?`\\color{|4||0.45}{${l}}`:l,u=[i,c,a&&`\\color{#52c11b|1||0.9}{${a}}`].filter(Boolean).join(" "),s=x(o)?.split("|")||[],f=s.includes("comment"),p=s.includes("version"),m=s.includes("author"),g=`## ${u}`;if(e.members.length){let t=[];e.members.forEach(e=>{let r=B(e.name.getText()),l=b(e),i=B(function(e){let t=e.initializer;return t&&n.isStringLiteral(t)?` '${t.text}'`:null}(e)),a=B(d(l)),c=B(k(l));!r||r.startsWith("\\color")||/^("|')(.+)("|')$/.test(r)||(r=`\\color{#4c81db}{${r}}`),t.push([r,!f&&c,i,!p&&a,!m&&(y(l)||y(o))]);// markdownContent += `| ${name} | ${comment} | ${value} | ${version} |`;
3
- });let r=!1,l=!1,i=!1;t.forEach(e=>{!f&&e[1]&&(i=!0),!p&&e[3]&&(l=!0),!m&&e[4]&&(r=!0)}),g+="\n";let a=["属性",i&&"说明","值",l&&"版本",r&&"作者"].filter(Boolean).join("|");g+=`|${a}|`;let c=[":-",i&&":-",":-",l&&":-",r&&":-"].filter(Boolean);g+="\n";let u=c.join("|");g+=`|${u}|`,t.forEach(e=>{g+="\n";let t=[e[0]||"-",i&&(e[1]||"-"),e[2]||"-",l&&(e[3]||"-"),r&&(e[4]||"-")].filter(Boolean).join("|");g+=`|${t}|`})}return g+="\n\n"}(e))}),E}
1
+ import { readFileSync } from 'fs';
2
+ import { dirname } from 'path';
3
+ import ts from 'typescript';
4
+ import { CONFIG } from './config.js';
5
+ const { ScriptKind, ScriptTarget, SyntaxKind, createSourceFile, forEachChild, getLeadingCommentRanges, isInterfaceDeclaration, isQuestionToken, isPropertySignature, isFunctionTypeNode, isUnionTypeNode, isMethodSignature } = ts;
6
+ const allType = {};
7
+ function getPropertyComment(propertyNode) {
8
+ const comments = getLeadingCommentRanges(propertyNode.getSourceFile().text, propertyNode.pos);
9
+ if (comments) {
10
+ const commentText = propertyNode.getSourceFile().text.substring(comments[0].pos, comments[0].end);
11
+ const match = commentText.match(/\/\*\*([\s\S]*?)\*\//);
12
+ if (match) {
13
+ const trimmedCommentText = match[1].replace(/^\s*\* ?/gm, '').replace(/\s+$/, '').trim();
14
+ return trimmedCommentText;
15
+ }
16
+ }
17
+ return null;
18
+ }
19
+ function getDefaultValueFromComment(commentText) {
20
+ if (!commentText) return null;
21
+ const defaultValueRegex = /@default\s+([^\n]+)/;
22
+ const defaultValueMatch = commentText.match(defaultValueRegex);
23
+ return defaultValueMatch ? defaultValueMatch[1].trim() : null;
24
+ }
25
+ function getVersionFromComment(commentText) {
26
+ if (!commentText) return null;
27
+ const versionRegex = /@since\s+([^\n]+)/;
28
+ const versionMatch = commentText.match(versionRegex);
29
+ return versionMatch ? versionMatch[1].trim() : null;
30
+ }
31
+ function getAuthor(commentText) {
32
+ if (!commentText) return null;
33
+ const regex = /@author (\w+)\s*(?:<([^>]+)>)?/;
34
+ const match = commentText.match(regex);
35
+ if (match?.length) {
36
+ const author = match[1].trim();
37
+ let url = match[2]?.trim();
38
+ const isEmail = /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(url);
39
+ if (!url) {
40
+ url = `https://github.com/${author}`;
41
+ } else if (isEmail) {
42
+ url = `mailto:${url}`;
43
+ }
44
+ return `[![${author}](https://avatars.githubusercontent.com/${author}?s=64)${author}](${url})`;
45
+ }
46
+ return null;
47
+ }
48
+ function getIgnore(commentText) {
49
+ if (!commentText) return null;
50
+ const versionRegex = /@ignore\s+([^\n]+)/;
51
+ const versionMatch = commentText.match(versionRegex);
52
+ return versionMatch ? versionMatch[1].trim() : null;
53
+ }
54
+ const regex = /(?<!['"])(unknown|any|void|bigint|object|undefined|null|boolean|number|string|symbol)(?!['"])/g;
55
+ function getTypeText(typeText, hasColor) {
56
+ if (!typeText) return null;
57
+ let modifiedTypeText = typeText.replace(/\b([A-Z][a-zA-Z0-9]*)\b/g, (item)=>{
58
+ if (allType[item]) {
59
+ const url = `/${[
60
+ CONFIG.basename,
61
+ allType[item]
62
+ ].join('/').split('/').filter(Boolean).join('/')}`;
63
+ return hasColor ? `[\\color{#009688}{${item}}](${url})` : `[${item}](${url})`;
64
+ }
65
+ return hasColor ? `\\color{#009688}{${item}}` : item;
66
+ });
67
+ if (hasColor) {
68
+ modifiedTypeText = modifiedTypeText.replace(regex, (match)=>{
69
+ return `\\color{#009688}{${match}}`;
70
+ });
71
+ }
72
+ return modifiedTypeText;
73
+ }
74
+ function getMemberValue(memberNode) {
75
+ const initializer = memberNode.initializer;
76
+ if (initializer && ts.isStringLiteral(initializer)) {
77
+ return ` '${initializer.text}'`;
78
+ }
79
+ return null;
80
+ }
81
+ function replacePairedSymbols(inputString) {
82
+ if (!inputString) return null;
83
+ const coloredContent = [];
84
+ let matches;
85
+ while(matches = /\\color{([^|}]*)\|?([^|}]*)\|?([^|}]*)\|?([^}]*)}{([^}]*)}/g.exec(inputString)){
86
+ coloredContent.push(matches[0]);
87
+ }
88
+ return inputString.replace(/(\{|\}|\[|\]|\(|\)|=>|keyof|typeof|true|false)/g, (match)=>{
89
+ if (coloredContent.some((content)=>content.includes(match))) {
90
+ return match;
91
+ }
92
+ return `\\color{#569cd6}{${match}}`;
93
+ });
94
+ }
95
+ function isFunctionTypeProperty(member) {
96
+ if (member.type) {
97
+ if (isUnionTypeNode(member.type)) {
98
+ for (const typeNode of member.type.types){
99
+ if (isFunctionTypeNode(typeNode)) {
100
+ return true;
101
+ }
102
+ }
103
+ } else {
104
+ return isFunctionTypeNode(member.type);
105
+ }
106
+ }
107
+ return false;
108
+ }
109
+ function replaceText(str) {
110
+ return replacePairedSymbols(str)?.replace(/^\s*\|\s*|\s*\|\s*$/gm, '').replace(/\n/g, '<br/>').replace(/\*/g, '\\*').replace(/\|/g, '\\|').replace(/(['"])((?:(?!\1).)*)\1/g, '\\color{#ce9178}{$1$2$1}');
111
+ }
112
+ function getBaseInterfaces(interfaceNode) {
113
+ const baseInterfaces = [];
114
+ if (interfaceNode.heritageClauses) {
115
+ for (const clause of interfaceNode.heritageClauses){
116
+ if (clause.token === ts.SyntaxKind.ExtendsKeyword) {
117
+ for (const typeNode of clause.types){
118
+ baseInterfaces.push(typeNode.getText());
119
+ }
120
+ }
121
+ }
122
+ }
123
+ return baseInterfaces;
124
+ }
125
+ function getComment(comment) {
126
+ if (!comment) return null;
127
+ return comment.replace(/^@[a-z].+/gm, '').replace(/(\n\s+)+/g, '<br />').replace(/\n/g, '<br />').replace(/(<br \/>)$/g, '');
128
+ }
129
+ function getMethodText(member) {
130
+ if (isMethodSignature(member)) {
131
+ return `(${member.parameters.map((param)=>`${param.name.getText()}: ${param.type?.getText() || 'any'}`).join(', ')}): ${member.type?.getText() || 'any'}`;
132
+ }
133
+ return member.type?.getText() || 'any';
134
+ }
135
+ function generateInterfaceDocumentation(node) {
136
+ const typeName = node.name.text;
137
+ Object.assign(allType, {
138
+ [node.name.text]: dirname(node.getSourceFile().fileName).replace(CONFIG.alias['@pkg'], '')
139
+ });
140
+ const baseComment = getPropertyComment(node);
141
+ const vers = getVersionFromComment(baseComment);
142
+ const title = getComment(baseComment);
143
+ const ignore = getIgnore(baseComment)?.split('|') || [];
144
+ const ignoreComment = ignore.includes('comment');
145
+ const ignoreInitial = ignore.includes('initial');
146
+ const ignoreOptional = ignore.includes('optional');
147
+ const ignoreVersion = ignore.includes('version');
148
+ const ignoreAuthor = ignore.includes('author');
149
+ const subTitle = title ? `\\color{|4||0.45}{${typeName}}` : typeName;
150
+ const baseInterfaces = getBaseInterfaces(node)?.map((s)=>{
151
+ return `<n-tag color="#4c81db" css=".tag{gap:0px;}">${getTypeText(replaceText(s))}</n-tag>`;
152
+ });
153
+ const heading = [
154
+ title,
155
+ subTitle,
156
+ vers && `\\color{#52c11b|1||0.9}{${vers}}`,
157
+ baseInterfaces.length > 0 && `<sub>\`extends\`</sub> ${baseInterfaces.join(' ')}`
158
+ ].filter(Boolean).join(' ');
159
+ let markdownContent = `## ${heading}`;
160
+ const members = node.members.filter(// 排除 never
161
+ (m)=>isPropertySignature(m) && m.type?.kind !== SyntaxKind.NeverKeyword || isMethodSignature(m));
162
+ const rowsData = [];
163
+ if (members.length) {
164
+ members.forEach((member)=>{
165
+ const type = getTypeText(replaceText(getMethodText(member)), true);
166
+ const propertyComment = getPropertyComment(member);
167
+ let name = replaceText(member.name.getText());
168
+ if (name && !name.startsWith('\\color')) {
169
+ if (isMethodSignature(member) || isFunctionTypeProperty(member) || type?.includes('=>')) {
170
+ name = `\\color{#f9a913}{${name}}`;
171
+ } else if (!/^("|')(.+)("|')$/.test(name)) {
172
+ name = `\\color{#4c81db}{${name}}`;
173
+ }
174
+ }
175
+ const isOptional = member.questionToken && isQuestionToken(member.questionToken);
176
+ rowsData.push([
177
+ name,
178
+ !ignoreOptional && `\\color{${isOptional ? '#f9a913' : '#52c11b'}\\|\\|\\|0.9}{${isOptional ? '✘' : '✔'}}`,
179
+ !ignoreComment && replaceText(getComment(propertyComment)),
180
+ type,
181
+ !ignoreInitial && replaceText(getDefaultValueFromComment(propertyComment)),
182
+ !ignoreVersion && replaceText(getVersionFromComment(propertyComment)),
183
+ !ignoreAuthor && (getAuthor(propertyComment) || getAuthor(baseComment))
184
+ ]);
185
+ });
186
+ let hasAuthor = false, hasVersion = false, hasInitial = false, hasComment = false;
187
+ rowsData.forEach((row)=>{
188
+ if (!ignoreComment && row[2]) {
189
+ hasComment = true;
190
+ }
191
+ if (!ignoreInitial && row[4]) {
192
+ hasInitial = true;
193
+ }
194
+ if (!ignoreVersion && row[5]) {
195
+ hasVersion = true;
196
+ }
197
+ if (!ignoreAuthor && row[6]) {
198
+ hasAuthor = true;
199
+ }
200
+ });
201
+ markdownContent += '\n';
202
+ const cols = [
203
+ '属性',
204
+ !ignoreOptional && '必要',
205
+ hasComment && '说明',
206
+ '类型',
207
+ hasInitial && '默认值',
208
+ hasVersion && '版本',
209
+ hasAuthor && '作者'
210
+ ].filter(Boolean).join('|');
211
+ markdownContent += `|${cols}|`;
212
+ const algins = [
213
+ ':-',
214
+ !ignoreOptional && ':-',
215
+ hasComment && ':-',
216
+ ':-',
217
+ hasInitial && ':-',
218
+ hasVersion && ':-',
219
+ hasAuthor && ':-'
220
+ ].filter(Boolean);
221
+ markdownContent += '\n';
222
+ const alignStr = algins.join('|');
223
+ markdownContent += `|${alignStr}|`;
224
+ rowsData.forEach((row)=>{
225
+ markdownContent += '\n';
226
+ const rowStr = [
227
+ row[0] || '-',
228
+ !ignoreOptional && (row[1] || '-'),
229
+ hasComment && (row[2] || '-'),
230
+ row[3] || '-',
231
+ hasInitial && (row[4] || '-'),
232
+ hasVersion && (row[5] || '-'),
233
+ hasAuthor && (row[6] || '-')
234
+ ].filter(Boolean).join('|');
235
+ markdownContent += `|${rowStr}|`;
236
+ });
237
+ markdownContent += '\n';
238
+ }
239
+ markdownContent += '\n';
240
+ return markdownContent;
241
+ }
242
+ function generateEnumDocumentation(node) {
243
+ const enumName = node.name.text;
244
+ const baseComment = getPropertyComment(node);
245
+ const title = getComment(baseComment);
246
+ const vers = getVersionFromComment(baseComment);
247
+ Object.assign(allType, {
248
+ [node.name.text]: dirname(node.getSourceFile().fileName).replace(CONFIG.alias['@pkg'], '')
249
+ });
250
+ const subTitle = title ? `\\color{|4||0.45}{${enumName}}` : enumName;
251
+ const heading = [
252
+ title,
253
+ subTitle,
254
+ vers && `\\color{#52c11b|1||0.9}{${vers}}`
255
+ ].filter(Boolean).join(' ');
256
+ const ignore = getIgnore(baseComment)?.split('|') || [];
257
+ const ignoreComment = ignore.includes('comment');
258
+ const ignoreVersion = ignore.includes('version');
259
+ const ignoreAuthor = ignore.includes('author');
260
+ let markdownContent = `## ${heading}`;
261
+ if (node.members.length) {
262
+ const rowsData = [];
263
+ node.members.forEach((member)=>{
264
+ let name = replaceText(member.name.getText());
265
+ const memberComment = getPropertyComment(member);
266
+ const value = replaceText(getMemberValue(member));
267
+ const version = replaceText(getVersionFromComment(memberComment));
268
+ const comment = replaceText(getComment(memberComment));
269
+ // markdownContent += '\n';
270
+ if (name && !name.startsWith('\\color') && !/^("|')(.+)("|')$/.test(name)) {
271
+ name = `\\color{#4c81db}{${name}}`;
272
+ }
273
+ rowsData.push([
274
+ name,
275
+ !ignoreComment && comment,
276
+ value,
277
+ !ignoreVersion && version,
278
+ !ignoreAuthor && (getAuthor(memberComment) || getAuthor(baseComment))
279
+ ]);
280
+ // markdownContent += `| ${name} | ${comment} | ${value} | ${version} |`;
281
+ });
282
+ let hasAuthor = false, hasVersion = false, hasComment = false;
283
+ rowsData.forEach((row)=>{
284
+ if (!ignoreComment && row[1]) {
285
+ hasComment = true;
286
+ }
287
+ if (!ignoreVersion && row[3]) {
288
+ hasVersion = true;
289
+ }
290
+ if (!ignoreAuthor && row[4]) {
291
+ hasAuthor = true;
292
+ }
293
+ });
294
+ markdownContent += '\n';
295
+ const cols = [
296
+ '属性',
297
+ hasComment && '说明',
298
+ '值',
299
+ hasVersion && '版本',
300
+ hasAuthor && '作者'
301
+ ].filter(Boolean).join('|');
302
+ markdownContent += `|${cols}|`;
303
+ const algins = [
304
+ ':-',
305
+ hasComment && ':-',
306
+ ':-',
307
+ hasVersion && ':-',
308
+ hasAuthor && ':-'
309
+ ].filter(Boolean);
310
+ markdownContent += '\n';
311
+ const alignStr = algins.join('|');
312
+ markdownContent += `|${alignStr}|`;
313
+ rowsData.forEach((row)=>{
314
+ markdownContent += '\n';
315
+ const rowStr = [
316
+ row[0] || '-',
317
+ hasComment && (row[1] || '-'),
318
+ row[2] || '-',
319
+ hasVersion && (row[3] || '-'),
320
+ hasAuthor && (row[4] || '-')
321
+ ].filter(Boolean).join('|');
322
+ markdownContent += `|${rowStr}|`;
323
+ });
324
+ }
325
+ markdownContent += '\n';
326
+ markdownContent += '\n';
327
+ return markdownContent;
328
+ }
329
+ export default function generateApi(path) {
330
+ const content = readFileSync(path, 'utf-8');
331
+ const sourceFile = createSourceFile(path, content, ScriptTarget.Latest, true, ScriptKind.TS);
332
+ let markdownDocumentation = '';
333
+ forEachChild(sourceFile, (node)=>{
334
+ if (isInterfaceDeclaration(node)) {
335
+ markdownDocumentation += generateInterfaceDocumentation(node);
336
+ } else if (ts.isEnumDeclaration(node)) {
337
+ markdownDocumentation += generateEnumDocumentation(node);
338
+ }
339
+ });
340
+ return markdownDocumentation;
341
+ }
package/lib/has-pkg.js CHANGED
@@ -1 +1,12 @@
1
- import{accessSync as o,constants as r}from"fs";import t from"./paths.js";export function hasPkg(a){let e=!1;try{o(`${t.programPath}/node_modules/${a}/package.json`,r.R_OK),e=!0}catch(o){e=!1}return e}
1
+ import { accessSync, constants } from 'fs';
2
+ import paths from './paths.js';
3
+ export function hasPkg(name) {
4
+ let flag = false;
5
+ try {
6
+ accessSync(`${paths.programPath}/node_modules/${name}/package.json`, constants.R_OK);
7
+ flag = true;
8
+ } catch (error) {
9
+ flag = false;
10
+ }
11
+ return flag;
12
+ }
@@ -1,4 +1,4 @@
1
- export default /**
1
+ /**
2
2
  * 向 html-webpack-plugin 导出的 HTML 模板 script 添加属性
3
3
  * ```javascript
4
4
  * // 假设输出 main chunk 为 main.[hash].chunk.js。
@@ -7,12 +7,33 @@ export default /**
7
7
  * return !!(src.match(/main\.(.*)\.bundle.js$/) || src.match('main.bundle.js'));
8
8
  * })),
9
9
  * ```
10
- */class{// eslint-disable-next-line no-unused-vars
11
- constructor(t){this.entryMatchCallback=t}apply(t){t.hooks.compilation.tap("AddEntryAttributeWebpackPlugin",a=>{// 通过最终的 webpack 配置的 plugins 属性,根据插件的 constructor.name 拿到 html-webpack-plugin 实例
12
- let e=t.options.plugins.map(({constructor:t})=>t).find(t=>t&&"HtmlWebpackPlugin"===t.name);// .find(
13
- // (plugin) => plugin instanceof HtmlWebpackPlugin,
14
- // ) as typeof HtmlWebpackPlugin;
15
- if(e){// 获取 html-webpack-plugin 所有的 hooks
16
- let t=e.getHooks(a);// 在插入标签之前做些什么
17
- t.alterAssetTagGroups.tap("AddEntryAttributeWebpackPlugin",t=>(// 拿到所有的标签,如果是 script 标签,并且满足我们的匹配函数,则将其 attributes['entry'] 设为 true
18
- t.headTags.forEach(t=>{"script"===t.tagName&&this.entryMatchCallback(t.attributes.src)&&(t.attributes.entry=!0)}),t))}})}}
10
+ */ class AddEntryAttributeWebpackPlugin {
11
+ // eslint-disable-next-line no-unused-vars
12
+ constructor(matchCallback){
13
+ this.entryMatchCallback = matchCallback;
14
+ }
15
+ apply(compiler) {
16
+ compiler.hooks.compilation.tap('AddEntryAttributeWebpackPlugin', (compilation)=>{
17
+ // 通过最终的 webpack 配置的 plugins 属性,根据插件的 constructor.name 拿到 html-webpack-plugin 实例
18
+ const HtmlWebpackPluginInstance = compiler.options.plugins.map(({ constructor })=>constructor).find((constructor)=>constructor && constructor.name === 'HtmlWebpackPlugin');
19
+ // .find(
20
+ // (plugin) => plugin instanceof HtmlWebpackPlugin,
21
+ // ) as typeof HtmlWebpackPlugin;
22
+ if (HtmlWebpackPluginInstance) {
23
+ // 获取 html-webpack-plugin 所有的 hooks
24
+ const hooks = HtmlWebpackPluginInstance.getHooks(compilation);
25
+ // 在插入标签之前做些什么
26
+ hooks.alterAssetTagGroups.tap('AddEntryAttributeWebpackPlugin', (data)=>{
27
+ // 拿到所有的标签,如果是 script 标签,并且满足我们的匹配函数,则将其 attributes['entry'] 设为 true
28
+ data.headTags.forEach((tag)=>{
29
+ if (tag.tagName === 'script' && this.entryMatchCallback(tag.attributes.src)) {
30
+ tag.attributes.entry = true;
31
+ }
32
+ });
33
+ return data;
34
+ });
35
+ }
36
+ });
37
+ }
38
+ }
39
+ export default AddEntryAttributeWebpackPlugin;