@lickle/docs 0.0.0-dev.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/README.md +3 -0
- package/dist/esm/_lib/fs/index.js +20 -0
- package/dist/esm/_lib/fs/index.js.map +1 -0
- package/dist/esm/_lib/fs/watch.js +30 -0
- package/dist/esm/_lib/fs/watch.js.map +1 -0
- package/dist/esm/_lib/index.js +9 -0
- package/dist/esm/_lib/index.js.map +1 -0
- package/dist/esm/_lib/jiti/index.js +16 -0
- package/dist/esm/_lib/jiti/index.js.map +1 -0
- package/dist/esm/_lib/path/index.js +42 -0
- package/dist/esm/_lib/path/index.js.map +1 -0
- package/dist/esm/_lib/pkg/index.js +128 -0
- package/dist/esm/_lib/pkg/index.js.map +1 -0
- package/dist/esm/_lib/repo/index.js +100 -0
- package/dist/esm/_lib/repo/index.js.map +1 -0
- package/dist/esm/_lib/slug/index.js +23 -0
- package/dist/esm/_lib/slug/index.js.map +1 -0
- package/dist/esm/_lib/t.js +2 -0
- package/dist/esm/_lib/t.js.map +1 -0
- package/dist/esm/_lib/tsconfig/index.js +12 -0
- package/dist/esm/_lib/tsconfig/index.js.map +1 -0
- package/dist/esm/_lib/util/index.js +54 -0
- package/dist/esm/_lib/util/index.js.map +1 -0
- package/dist/esm/cli/cmd/dev.js +74 -0
- package/dist/esm/cli/cmd/dev.js.map +1 -0
- package/dist/esm/cli/cmd/index.js +4 -0
- package/dist/esm/cli/cmd/index.js.map +1 -0
- package/dist/esm/cli/cmd/init.js +74 -0
- package/dist/esm/cli/cmd/init.js.map +1 -0
- package/dist/esm/cli/cmd/json.js +28 -0
- package/dist/esm/cli/cmd/json.js.map +1 -0
- package/dist/esm/cli/env.js +5 -0
- package/dist/esm/cli/env.js.map +1 -0
- package/dist/esm/cli/index.js +13 -0
- package/dist/esm/cli/index.js.map +1 -0
- package/dist/esm/cli/vite/client/index.jsx +20 -0
- package/dist/esm/cli/vite/client/index.jsx.map +1 -0
- package/dist/esm/cli/vite/index.js +100 -0
- package/dist/esm/cli/vite/index.js.map +1 -0
- package/dist/esm/cli.js +5 -0
- package/dist/esm/cli.js.map +1 -0
- package/dist/esm/config/defaults.js +32 -0
- package/dist/esm/config/defaults.js.map +1 -0
- package/dist/esm/config/file.js +48 -0
- package/dist/esm/config/file.js.map +1 -0
- package/dist/esm/config/index.js +7 -0
- package/dist/esm/config/index.js.map +1 -0
- package/dist/esm/config/load.js +79 -0
- package/dist/esm/config/load.js.map +1 -0
- package/dist/esm/config/types.js +2 -0
- package/dist/esm/config/types.js.map +1 -0
- package/dist/esm/core/index.js +3 -0
- package/dist/esm/core/index.js.map +1 -0
- package/dist/esm/core/project/debug.js +26 -0
- package/dist/esm/core/project/debug.js.map +1 -0
- package/dist/esm/core/project/index.js +39 -0
- package/dist/esm/core/project/index.js.map +1 -0
- package/dist/esm/core/project/naming.js +39 -0
- package/dist/esm/core/project/naming.js.map +1 -0
- package/dist/esm/core/project/routing.js +173 -0
- package/dist/esm/core/project/routing.js.map +1 -0
- package/dist/esm/core/project/types.js +4 -0
- package/dist/esm/core/project/types.js.map +1 -0
- package/dist/esm/core/reflect/index.js +10 -0
- package/dist/esm/core/reflect/index.js.map +1 -0
- package/dist/esm/core/reflect/indexed.js +195 -0
- package/dist/esm/core/reflect/indexed.js.map +1 -0
- package/dist/esm/core/reflect/resolve.js +157 -0
- package/dist/esm/core/reflect/resolve.js.map +1 -0
- package/dist/esm/core/reflect/scan.js +787 -0
- package/dist/esm/core/reflect/scan.js.map +1 -0
- package/dist/esm/core/reflect/state.js +29 -0
- package/dist/esm/core/reflect/state.js.map +1 -0
- package/dist/esm/core/reflect/types.js +16 -0
- package/dist/esm/core/reflect/types.js.map +1 -0
- package/dist/esm/core/reflect/walk.js +106 -0
- package/dist/esm/core/reflect/walk.js.map +1 -0
- package/dist/esm/solidjs/index.js +2 -0
- package/dist/esm/solidjs/index.js.map +1 -0
- package/dist/esm/solidjs/jsx-runtime.js +2 -0
- package/dist/esm/solidjs/jsx-runtime.js.map +1 -0
- package/dist/esm/ui/App.jsx +71 -0
- package/dist/esm/ui/App.jsx.map +1 -0
- package/dist/esm/ui/components/Breadcrumb.jsx +45 -0
- package/dist/esm/ui/components/Breadcrumb.jsx.map +1 -0
- package/dist/esm/ui/components/Code/index.jsx +89 -0
- package/dist/esm/ui/components/Code/index.jsx.map +1 -0
- package/dist/esm/ui/components/Comment.jsx +176 -0
- package/dist/esm/ui/components/Comment.jsx.map +1 -0
- package/dist/esm/ui/components/Declaration.jsx +147 -0
- package/dist/esm/ui/components/Declaration.jsx.map +1 -0
- package/dist/esm/ui/components/Header.jsx +66 -0
- package/dist/esm/ui/components/Header.jsx.map +1 -0
- package/dist/esm/ui/components/Layout.jsx +44 -0
- package/dist/esm/ui/components/Layout.jsx.map +1 -0
- package/dist/esm/ui/components/Link.jsx +34 -0
- package/dist/esm/ui/components/Link.jsx.map +1 -0
- package/dist/esm/ui/components/LivePreview/Example.jsx +76 -0
- package/dist/esm/ui/components/LivePreview/Example.jsx.map +1 -0
- package/dist/esm/ui/components/LivePreview/Sandbox.jsx +25 -0
- package/dist/esm/ui/components/LivePreview/Sandbox.jsx.map +1 -0
- package/dist/esm/ui/components/LivePreview/index.js +4 -0
- package/dist/esm/ui/components/LivePreview/index.js.map +1 -0
- package/dist/esm/ui/components/LivePreview/transform.js +16 -0
- package/dist/esm/ui/components/LivePreview/transform.js.map +1 -0
- package/dist/esm/ui/components/Markdown.jsx +14 -0
- package/dist/esm/ui/components/Markdown.jsx.map +1 -0
- package/dist/esm/ui/components/Page.jsx +121 -0
- package/dist/esm/ui/components/Page.jsx.map +1 -0
- package/dist/esm/ui/components/References.jsx +32 -0
- package/dist/esm/ui/components/References.jsx.map +1 -0
- package/dist/esm/ui/components/SearchPalette.jsx +178 -0
- package/dist/esm/ui/components/SearchPalette.jsx.map +1 -0
- package/dist/esm/ui/components/Sidebar.jsx +63 -0
- package/dist/esm/ui/components/Sidebar.jsx.map +1 -0
- package/dist/esm/ui/components/Syntax.jsx +10 -0
- package/dist/esm/ui/components/Syntax.jsx.map +1 -0
- package/dist/esm/ui/components/ThemeToggle.jsx +25 -0
- package/dist/esm/ui/components/ThemeToggle.jsx.map +1 -0
- package/dist/esm/ui/components/Type.jsx +444 -0
- package/dist/esm/ui/components/Type.jsx.map +1 -0
- package/dist/esm/ui/components/index.js +16 -0
- package/dist/esm/ui/components/index.js.map +1 -0
- package/dist/esm/ui/context/components.jsx +30 -0
- package/dist/esm/ui/context/components.jsx.map +1 -0
- package/dist/esm/ui/context/global.js +15 -0
- package/dist/esm/ui/context/global.js.map +1 -0
- package/dist/esm/ui/context/index.js +5 -0
- package/dist/esm/ui/context/index.js.map +1 -0
- package/dist/esm/ui/context/markup/index.jsx +25 -0
- package/dist/esm/ui/context/markup/index.jsx.map +1 -0
- package/dist/esm/ui/context/markup/markdown.js +34 -0
- package/dist/esm/ui/context/markup/markdown.js.map +1 -0
- package/dist/esm/ui/context/markup/shiki.js +58 -0
- package/dist/esm/ui/context/markup/shiki.js.map +1 -0
- package/dist/esm/ui/context/markup/util.js +22 -0
- package/dist/esm/ui/context/markup/util.js.map +1 -0
- package/dist/esm/ui/context/project/index.jsx +27 -0
- package/dist/esm/ui/context/project/index.jsx.map +1 -0
- package/dist/esm/ui/context/project/indexed.js +47 -0
- package/dist/esm/ui/context/project/indexed.js.map +1 -0
- package/dist/esm/ui/context/project/types.js +2 -0
- package/dist/esm/ui/context/project/types.js.map +1 -0
- package/dist/esm/ui/context/theme.jsx +36 -0
- package/dist/esm/ui/context/theme.jsx.map +1 -0
- package/dist/esm/ui/hooks/index.js +94 -0
- package/dist/esm/ui/hooks/index.js.map +1 -0
- package/dist/esm/ui/index.js +5 -0
- package/dist/esm/ui/index.js.map +1 -0
- package/dist/esm/ui/renderer.jsx +21 -0
- package/dist/esm/ui/renderer.jsx.map +1 -0
- package/dist/esm/ui/util/comment.js +14 -0
- package/dist/esm/ui/util/comment.js.map +1 -0
- package/dist/esm/ui/util/kind.js +85 -0
- package/dist/esm/ui/util/kind.js.map +1 -0
- package/dist/esm/ui/util/markdown.js +66 -0
- package/dist/esm/ui/util/markdown.js.map +1 -0
- package/dist/esm/ui/util/search.js +75 -0
- package/dist/esm/ui/util/search.js.map +1 -0
- package/dist/ts/_lib/fs/index.d.ts +6 -0
- package/dist/ts/_lib/fs/index.d.ts.map +1 -0
- package/dist/ts/_lib/fs/watch.d.ts +14 -0
- package/dist/ts/_lib/fs/watch.d.ts.map +1 -0
- package/dist/ts/_lib/index.d.ts +10 -0
- package/dist/ts/_lib/index.d.ts.map +1 -0
- package/dist/ts/_lib/jiti/index.d.ts +2 -0
- package/dist/ts/_lib/jiti/index.d.ts.map +1 -0
- package/dist/ts/_lib/path/index.d.ts +12 -0
- package/dist/ts/_lib/path/index.d.ts.map +1 -0
- package/dist/ts/_lib/pkg/index.d.ts +28 -0
- package/dist/ts/_lib/pkg/index.d.ts.map +1 -0
- package/dist/ts/_lib/repo/index.d.ts +30 -0
- package/dist/ts/_lib/repo/index.d.ts.map +1 -0
- package/dist/ts/_lib/slug/index.d.ts +4 -0
- package/dist/ts/_lib/slug/index.d.ts.map +1 -0
- package/dist/ts/_lib/t.d.ts +11 -0
- package/dist/ts/_lib/t.d.ts.map +1 -0
- package/dist/ts/_lib/tsconfig/index.d.ts +8 -0
- package/dist/ts/_lib/tsconfig/index.d.ts.map +1 -0
- package/dist/ts/_lib/util/index.d.ts +9 -0
- package/dist/ts/_lib/util/index.d.ts.map +1 -0
- package/dist/ts/cli/cmd/dev.d.ts +27 -0
- package/dist/ts/cli/cmd/dev.d.ts.map +1 -0
- package/dist/ts/cli/cmd/index.d.ts +4 -0
- package/dist/ts/cli/cmd/index.d.ts.map +1 -0
- package/dist/ts/cli/cmd/init.d.ts +14 -0
- package/dist/ts/cli/cmd/init.d.ts.map +1 -0
- package/dist/ts/cli/cmd/json.d.ts +12 -0
- package/dist/ts/cli/cmd/json.d.ts.map +1 -0
- package/dist/ts/cli/env.d.ts +2 -0
- package/dist/ts/cli/env.d.ts.map +1 -0
- package/dist/ts/cli/index.d.ts +78 -0
- package/dist/ts/cli/index.d.ts.map +1 -0
- package/dist/ts/cli/vite/client/index.d.ts +10 -0
- package/dist/ts/cli/vite/client/index.d.ts.map +1 -0
- package/dist/ts/cli/vite/index.d.ts +12 -0
- package/dist/ts/cli/vite/index.d.ts.map +1 -0
- package/dist/ts/cli.d.ts +3 -0
- package/dist/ts/cli.d.ts.map +1 -0
- package/dist/ts/config/defaults.d.ts +10 -0
- package/dist/ts/config/defaults.d.ts.map +1 -0
- package/dist/ts/config/file.d.ts +3 -0
- package/dist/ts/config/file.d.ts.map +1 -0
- package/dist/ts/config/index.d.ts +6 -0
- package/dist/ts/config/index.d.ts.map +1 -0
- package/dist/ts/config/load.d.ts +36 -0
- package/dist/ts/config/load.d.ts.map +1 -0
- package/dist/ts/config/types.d.ts +56 -0
- package/dist/ts/config/types.d.ts.map +1 -0
- package/dist/ts/core/index.d.ts +3 -0
- package/dist/ts/core/index.d.ts.map +1 -0
- package/dist/ts/core/project/debug.d.ts +3 -0
- package/dist/ts/core/project/debug.d.ts.map +1 -0
- package/dist/ts/core/project/index.d.ts +17 -0
- package/dist/ts/core/project/index.d.ts.map +1 -0
- package/dist/ts/core/project/naming.d.ts +23 -0
- package/dist/ts/core/project/naming.d.ts.map +1 -0
- package/dist/ts/core/project/routing.d.ts +61 -0
- package/dist/ts/core/project/routing.d.ts.map +1 -0
- package/dist/ts/core/project/types.d.ts +56 -0
- package/dist/ts/core/project/types.d.ts.map +1 -0
- package/dist/ts/core/reflect/index.d.ts +22 -0
- package/dist/ts/core/reflect/index.d.ts.map +1 -0
- package/dist/ts/core/reflect/indexed.d.ts +83 -0
- package/dist/ts/core/reflect/indexed.d.ts.map +1 -0
- package/dist/ts/core/reflect/resolve.d.ts +3 -0
- package/dist/ts/core/reflect/resolve.d.ts.map +1 -0
- package/dist/ts/core/reflect/scan.d.ts +218 -0
- package/dist/ts/core/reflect/scan.d.ts.map +1 -0
- package/dist/ts/core/reflect/state.d.ts +44 -0
- package/dist/ts/core/reflect/state.d.ts.map +1 -0
- package/dist/ts/core/reflect/types.d.ts +289 -0
- package/dist/ts/core/reflect/types.d.ts.map +1 -0
- package/dist/ts/core/reflect/walk.d.ts +28 -0
- package/dist/ts/core/reflect/walk.d.ts.map +1 -0
- package/dist/ts/solidjs/index.d.ts +2 -0
- package/dist/ts/solidjs/index.d.ts.map +1 -0
- package/dist/ts/solidjs/jsx-runtime.d.ts +2 -0
- package/dist/ts/solidjs/jsx-runtime.d.ts.map +1 -0
- package/dist/ts/ui/App.d.ts +17 -0
- package/dist/ts/ui/App.d.ts.map +1 -0
- package/dist/ts/ui/components/Breadcrumb.d.ts +4 -0
- package/dist/ts/ui/components/Breadcrumb.d.ts.map +1 -0
- package/dist/ts/ui/components/Code/index.d.ts +17 -0
- package/dist/ts/ui/components/Code/index.d.ts.map +1 -0
- package/dist/ts/ui/components/Comment.d.ts +49 -0
- package/dist/ts/ui/components/Comment.d.ts.map +1 -0
- package/dist/ts/ui/components/Declaration.d.ts +122 -0
- package/dist/ts/ui/components/Declaration.d.ts.map +1 -0
- package/dist/ts/ui/components/Header.d.ts +5 -0
- package/dist/ts/ui/components/Header.d.ts.map +1 -0
- package/dist/ts/ui/components/Layout.d.ts +5 -0
- package/dist/ts/ui/components/Layout.d.ts.map +1 -0
- package/dist/ts/ui/components/Link.d.ts +15 -0
- package/dist/ts/ui/components/Link.d.ts.map +1 -0
- package/dist/ts/ui/components/LivePreview/Example.d.ts +16 -0
- package/dist/ts/ui/components/LivePreview/Example.d.ts.map +1 -0
- package/dist/ts/ui/components/LivePreview/Sandbox.d.ts +20 -0
- package/dist/ts/ui/components/LivePreview/Sandbox.d.ts.map +1 -0
- package/dist/ts/ui/components/LivePreview/index.d.ts +4 -0
- package/dist/ts/ui/components/LivePreview/index.d.ts.map +1 -0
- package/dist/ts/ui/components/LivePreview/transform.d.ts +19 -0
- package/dist/ts/ui/components/LivePreview/transform.d.ts.map +1 -0
- package/dist/ts/ui/components/Markdown.d.ts +11 -0
- package/dist/ts/ui/components/Markdown.d.ts.map +1 -0
- package/dist/ts/ui/components/Page.d.ts +21 -0
- package/dist/ts/ui/components/Page.d.ts.map +1 -0
- package/dist/ts/ui/components/References.d.ts +2 -0
- package/dist/ts/ui/components/References.d.ts.map +1 -0
- package/dist/ts/ui/components/SearchPalette.d.ts +5 -0
- package/dist/ts/ui/components/SearchPalette.d.ts.map +1 -0
- package/dist/ts/ui/components/Sidebar.d.ts +5 -0
- package/dist/ts/ui/components/Sidebar.d.ts.map +1 -0
- package/dist/ts/ui/components/Syntax.d.ts +15 -0
- package/dist/ts/ui/components/Syntax.d.ts.map +1 -0
- package/dist/ts/ui/components/ThemeToggle.d.ts +2 -0
- package/dist/ts/ui/components/ThemeToggle.d.ts.map +1 -0
- package/dist/ts/ui/components/Type.d.ts +82 -0
- package/dist/ts/ui/components/Type.d.ts.map +1 -0
- package/dist/ts/ui/components/index.d.ts +15 -0
- package/dist/ts/ui/components/index.d.ts.map +1 -0
- package/dist/ts/ui/context/components.d.ts +124 -0
- package/dist/ts/ui/context/components.d.ts.map +1 -0
- package/dist/ts/ui/context/global.d.ts +210 -0
- package/dist/ts/ui/context/global.d.ts.map +1 -0
- package/dist/ts/ui/context/index.d.ts +5 -0
- package/dist/ts/ui/context/index.d.ts.map +1 -0
- package/dist/ts/ui/context/markup/index.d.ts +20 -0
- package/dist/ts/ui/context/markup/index.d.ts.map +1 -0
- package/dist/ts/ui/context/markup/markdown.d.ts +4 -0
- package/dist/ts/ui/context/markup/markdown.d.ts.map +1 -0
- package/dist/ts/ui/context/markup/shiki.d.ts +17 -0
- package/dist/ts/ui/context/markup/shiki.d.ts.map +1 -0
- package/dist/ts/ui/context/markup/util.d.ts +11 -0
- package/dist/ts/ui/context/markup/util.d.ts.map +1 -0
- package/dist/ts/ui/context/project/index.d.ts +25 -0
- package/dist/ts/ui/context/project/index.d.ts.map +1 -0
- package/dist/ts/ui/context/project/indexed.d.ts +4 -0
- package/dist/ts/ui/context/project/indexed.d.ts.map +1 -0
- package/dist/ts/ui/context/project/types.d.ts +13 -0
- package/dist/ts/ui/context/project/types.d.ts.map +1 -0
- package/dist/ts/ui/context/theme.d.ts +12 -0
- package/dist/ts/ui/context/theme.d.ts.map +1 -0
- package/dist/ts/ui/hooks/index.d.ts +29 -0
- package/dist/ts/ui/hooks/index.d.ts.map +1 -0
- package/dist/ts/ui/index.d.ts +5 -0
- package/dist/ts/ui/index.d.ts.map +1 -0
- package/dist/ts/ui/renderer.d.ts +200 -0
- package/dist/ts/ui/renderer.d.ts.map +1 -0
- package/dist/ts/ui/util/comment.d.ts +4 -0
- package/dist/ts/ui/util/comment.d.ts.map +1 -0
- package/dist/ts/ui/util/kind.d.ts +15 -0
- package/dist/ts/ui/util/kind.d.ts.map +1 -0
- package/dist/ts/ui/util/markdown.d.ts +8 -0
- package/dist/ts/ui/util/markdown.d.ts.map +1 -0
- package/dist/ts/ui/util/search.d.ts +20 -0
- package/dist/ts/ui/util/search.d.ts.map +1 -0
- package/package.json +89 -0
- package/src/_lib/fs/index.ts +23 -0
- package/src/_lib/fs/watch.ts +40 -0
- package/src/_lib/index.ts +9 -0
- package/src/_lib/jiti/index.ts +18 -0
- package/src/_lib/path/index.ts +44 -0
- package/src/_lib/pkg/index.ts +165 -0
- package/src/_lib/repo/index.ts +138 -0
- package/src/_lib/slug/index.ts +26 -0
- package/src/_lib/t.ts +17 -0
- package/src/_lib/tsconfig/index.ts +20 -0
- package/src/_lib/util/index.ts +53 -0
- package/src/cli/cmd/dev.ts +86 -0
- package/src/cli/cmd/index.ts +3 -0
- package/src/cli/cmd/init.ts +80 -0
- package/src/cli/cmd/json.ts +28 -0
- package/src/cli/env.ts +6 -0
- package/src/cli/index.ts +14 -0
- package/src/cli/vite/client/index.html +27 -0
- package/src/cli/vite/client/index.tsx +30 -0
- package/src/cli/vite/client/public/apple-touch-icon.png +0 -0
- package/src/cli/vite/client/public/favicon-96x96.png +0 -0
- package/src/cli/vite/client/public/favicon.ico +0 -0
- package/src/cli/vite/client/public/favicon.svg +1 -0
- package/src/cli/vite/client/public/site.webmanifest +21 -0
- package/src/cli/vite/client/public/web-app-manifest-192x192.png +0 -0
- package/src/cli/vite/client/public/web-app-manifest-512x512.png +0 -0
- package/src/cli/vite/index.ts +115 -0
- package/src/cli.ts +6 -0
- package/src/config/defaults.ts +36 -0
- package/src/config/file.ts +53 -0
- package/src/config/index.ts +11 -0
- package/src/config/load.ts +95 -0
- package/src/config/types.ts +59 -0
- package/src/core/index.ts +2 -0
- package/src/core/project/debug.ts +30 -0
- package/src/core/project/index.ts +58 -0
- package/src/core/project/naming.ts +49 -0
- package/src/core/project/routing.ts +234 -0
- package/src/core/project/types.ts +47 -0
- package/src/core/reflect/index.ts +18 -0
- package/src/core/reflect/indexed.ts +242 -0
- package/src/core/reflect/resolve.ts +159 -0
- package/src/core/reflect/scan.ts +816 -0
- package/src/core/reflect/state.ts +75 -0
- package/src/core/reflect/types.ts +164 -0
- package/src/core/reflect/walk.ts +121 -0
- package/src/solidjs/index.ts +1 -0
- package/src/solidjs/jsx-runtime.ts +1 -0
- package/src/ui/App.tsx +115 -0
- package/src/ui/components/Breadcrumb.tsx +53 -0
- package/src/ui/components/Code/index.tsx +112 -0
- package/src/ui/components/Comment.tsx +221 -0
- package/src/ui/components/Declaration.tsx +210 -0
- package/src/ui/components/Header.tsx +99 -0
- package/src/ui/components/Layout.tsx +62 -0
- package/src/ui/components/Link.tsx +43 -0
- package/src/ui/components/LivePreview/Example.tsx +104 -0
- package/src/ui/components/LivePreview/Sandbox.tsx +36 -0
- package/src/ui/components/LivePreview/index.ts +3 -0
- package/src/ui/components/LivePreview/transform.ts +28 -0
- package/src/ui/components/Markdown.tsx +16 -0
- package/src/ui/components/Page.tsx +162 -0
- package/src/ui/components/References.tsx +34 -0
- package/src/ui/components/SearchPalette.tsx +266 -0
- package/src/ui/components/Sidebar.tsx +107 -0
- package/src/ui/components/Syntax.tsx +10 -0
- package/src/ui/components/ThemeToggle.tsx +50 -0
- package/src/ui/components/Type.tsx +583 -0
- package/src/ui/components/index.ts +15 -0
- package/src/ui/context/components.tsx +103 -0
- package/src/ui/context/global.ts +33 -0
- package/src/ui/context/index.ts +4 -0
- package/src/ui/context/markup/index.tsx +39 -0
- package/src/ui/context/markup/markdown.ts +37 -0
- package/src/ui/context/markup/shiki.ts +72 -0
- package/src/ui/context/markup/util.ts +20 -0
- package/src/ui/context/project/index.tsx +47 -0
- package/src/ui/context/project/indexed.ts +52 -0
- package/src/ui/context/project/types.ts +14 -0
- package/src/ui/context/theme.tsx +45 -0
- package/src/ui/hooks/index.ts +116 -0
- package/src/ui/index.ts +4 -0
- package/src/ui/renderer.tsx +31 -0
- package/src/ui/util/comment.ts +12 -0
- package/src/ui/util/kind.ts +120 -0
- package/src/ui/util/markdown.ts +74 -0
- package/src/ui/util/search.ts +84 -0
- package/theme.css +301 -0
- package/tsconfig.client.json +12 -0
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
import { makeScanState, type ScanOptions, type ScanState as State } from './state.ts'
|
|
2
|
+
import ts from 'typescript'
|
|
3
|
+
|
|
4
|
+
import type * as T from './types.ts'
|
|
5
|
+
|
|
6
|
+
export const scan = (rootFiles: string[], options: ScanOptions) => {
|
|
7
|
+
const compilerOptions: ts.CompilerOptions = { jsx: ts.JsxEmit.ReactJSX, ...options.compilerOptions }
|
|
8
|
+
const program = ts.createProgram(rootFiles, compilerOptions)
|
|
9
|
+
const checker = program.getTypeChecker()
|
|
10
|
+
const s = makeScanState(checker, options)
|
|
11
|
+
|
|
12
|
+
const files = new Array<ts.SourceFile>()
|
|
13
|
+
for (const file of rootFiles) {
|
|
14
|
+
const sf = program.getSourceFile(file)
|
|
15
|
+
if (!sf) continue
|
|
16
|
+
files.push(sf)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
while (files.length) {
|
|
20
|
+
const sf = files.shift()!
|
|
21
|
+
scan.SourceFile(s, sf, files)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return s
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
scan.SourceFile = (s: State, node: ts.SourceFile, queue: ts.SourceFile[]) => {
|
|
28
|
+
if (s.seen.has(node) || !s.include(node)) return
|
|
29
|
+
s.seen.add(node)
|
|
30
|
+
s.parent = s.root
|
|
31
|
+
const f = statement(s, node, 'module', () => ({ path: s.getPath(node) }))
|
|
32
|
+
s.parent = f.id
|
|
33
|
+
node.statements.forEach((stmt) => {
|
|
34
|
+
if (ts.isExportDeclaration(stmt)) return scan.ExportDeclaration(s, stmt, queue)
|
|
35
|
+
if (ts.isExportAssignment(stmt)) return scan.ExportAssignment(s, stmt)
|
|
36
|
+
scan.Statement(s, stmt)
|
|
37
|
+
})
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
scan.Statement = (s: State, node: ts.Statement) => {
|
|
42
|
+
if (ts.isVariableStatement(node)) {
|
|
43
|
+
return node.declarationList.declarations.forEach((d) => scan.VariableDeclaration(s, d))
|
|
44
|
+
}
|
|
45
|
+
if (ts.isVariableDeclaration(node)) return scan.VariableDeclaration(s, node)
|
|
46
|
+
if (ts.isFunctionDeclaration(node)) return scan.FunctionDeclaration(s, node)
|
|
47
|
+
if (ts.isClassDeclaration(node)) return scan.ClassDeclaration(s, node)
|
|
48
|
+
if (ts.isInterfaceDeclaration(node)) return scan.InterfaceDeclaration(s, node)
|
|
49
|
+
if (ts.isTypeAliasDeclaration(node)) return scan.TypeAliasDeclaration(s, node)
|
|
50
|
+
if (ts.isEnumDeclaration(node)) return scan.EnumDeclaration(s, node)
|
|
51
|
+
if (ts.isModuleDeclaration(node)) return scan.ModuleDeclaration(s, node)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
scan.VariableDeclaration = (s: State, node: ts.VariableDeclaration) => {
|
|
55
|
+
const init = node.initializer
|
|
56
|
+
if (init && (ts.isArrowFunction(init) || ts.isFunctionExpression(init))) {
|
|
57
|
+
return statement(s, node, 'function', () => functionBody(s, init))
|
|
58
|
+
}
|
|
59
|
+
return statement(s, node, 'variable', () => ({
|
|
60
|
+
type: node.type ? scan.Type(s, node.type) : inferAt(s, node),
|
|
61
|
+
defaultValue: node.initializer?.getText(),
|
|
62
|
+
}))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
scan.FunctionDeclaration = (s: State, decl: ts.FunctionDeclaration) => {
|
|
66
|
+
return statement(s, decl, 'function', () => functionBody(s, decl))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
scan.ClassDeclaration = (s: State, node: ts.ClassDeclaration) => {
|
|
70
|
+
const constructors: T.Part<'signature'>[] = []
|
|
71
|
+
const properties: T.Part<'property'>[] = []
|
|
72
|
+
const methods: T.Part<'method'>[] = []
|
|
73
|
+
let indexSignature: T.Part<'index-signature'> | undefined
|
|
74
|
+
for (const m of node.members) {
|
|
75
|
+
if (ts.isConstructorDeclaration(m)) constructors.push(signature(s, m))
|
|
76
|
+
else if (ts.isPropertyDeclaration(m) && ts.isIdentifier(m.name)) properties.push(property(s, m))
|
|
77
|
+
else if (ts.isMethodDeclaration(m) && ts.isIdentifier(m.name)) methods.push(method(s, m))
|
|
78
|
+
else if (ts.isIndexSignatureDeclaration(m)) indexSignature = indexSignatureDecl(s, m)
|
|
79
|
+
}
|
|
80
|
+
return statement(s, node, 'class', () => ({
|
|
81
|
+
...generics(s, node),
|
|
82
|
+
...heritage(s, node),
|
|
83
|
+
constructors,
|
|
84
|
+
properties,
|
|
85
|
+
methods,
|
|
86
|
+
...(indexSignature ? { indexSignature } : {}),
|
|
87
|
+
}))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
scan.InterfaceDeclaration = (s: State, node: ts.InterfaceDeclaration) =>
|
|
91
|
+
statement(s, node, 'interface', () => ({
|
|
92
|
+
...generics(s, node),
|
|
93
|
+
...interfaceExtends(s, node),
|
|
94
|
+
...objectMembers(s, node.members),
|
|
95
|
+
}))
|
|
96
|
+
|
|
97
|
+
scan.TypeAliasDeclaration = (s: State, node: ts.TypeAliasDeclaration) =>
|
|
98
|
+
statement(s, node, 'type-alias', () => ({
|
|
99
|
+
...generics(s, node),
|
|
100
|
+
type: scan.Type(s, node.type),
|
|
101
|
+
}))
|
|
102
|
+
|
|
103
|
+
scan.EnumDeclaration = (s: State, node: ts.EnumDeclaration) =>
|
|
104
|
+
statement(s, node, 'enum', () => ({
|
|
105
|
+
const: !!node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ConstKeyword),
|
|
106
|
+
members: node.members.map((m) => enumMember(s, m)),
|
|
107
|
+
}))
|
|
108
|
+
|
|
109
|
+
scan.ModuleDeclaration = (s: State, node: ts.ModuleDeclaration) => {
|
|
110
|
+
const ns = statement(s, node, 'namespace', () => ({}))
|
|
111
|
+
const body = node.body
|
|
112
|
+
if (!body) return ns
|
|
113
|
+
|
|
114
|
+
const prev = s.parent
|
|
115
|
+
s.parent = ns.id
|
|
116
|
+
if (ts.isModuleBlock(body)) {
|
|
117
|
+
body.statements.forEach((stmt) => scan.Statement(s, stmt))
|
|
118
|
+
} else if (ts.isModuleDeclaration(body)) {
|
|
119
|
+
// `namespace A.B { … }` → recurse on the inner `B`.
|
|
120
|
+
scan.ModuleDeclaration(s, body)
|
|
121
|
+
}
|
|
122
|
+
s.parent = prev
|
|
123
|
+
return ns
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
scan.ExportDeclaration = (s: State, node: ts.ExportDeclaration, queue: ts.SourceFile[]) => {
|
|
127
|
+
const spec = node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : undefined
|
|
128
|
+
|
|
129
|
+
// Enqueue the source module so its declarations exist for resolution.
|
|
130
|
+
if (spec) {
|
|
131
|
+
const sym = s.checker.getSymbolAtLocation(node.moduleSpecifier!)
|
|
132
|
+
const decl = sym?.valueDeclaration ?? sym?.declarations?.[0]
|
|
133
|
+
if (decl && ts.isSourceFile(decl) && !s.seen.has(decl) && s.include(decl)) {
|
|
134
|
+
queue.push(decl)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Emit an `export` declaration node (ref filled by resolver).
|
|
139
|
+
const exp = statement(s, node, 'export', () => ({ names: [], star: false }))
|
|
140
|
+
s.exports.push(exp)
|
|
141
|
+
|
|
142
|
+
if (!node.exportClause) {
|
|
143
|
+
if (!spec) return
|
|
144
|
+
s.exportsForm.set(exp.id, 'star')
|
|
145
|
+
s.exportsSpec.set(exp.id, spec)
|
|
146
|
+
s.exportsOrigin.set(exp.id, node)
|
|
147
|
+
exp.star = true
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
if (ts.isNamespaceExport(node.exportClause)) {
|
|
151
|
+
if (!spec) return
|
|
152
|
+
s.exportsForm.set(exp.id, 'namespace-from')
|
|
153
|
+
s.exportsSpec.set(exp.id, spec)
|
|
154
|
+
s.exportsAlias.set(exp.id, node.exportClause.name.text)
|
|
155
|
+
s.exportsOrigin.set(exp.id, node)
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
const entries = node.exportClause.elements.map((el) => ({
|
|
159
|
+
name: (el.propertyName ?? el.name).text,
|
|
160
|
+
...(el.propertyName ? { as: el.name.text } : {}),
|
|
161
|
+
}))
|
|
162
|
+
s.exportsForm.set(exp.id, spec ? 'named-from' : 'named-local')
|
|
163
|
+
if (spec) s.exportsSpec.set(exp.id, spec)
|
|
164
|
+
s.exportsEntries.set(exp.id, entries)
|
|
165
|
+
s.exportsOrigin.set(exp.id, node)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// `export default <expr>` / `export = <expr>`. The target is resolved later.
|
|
169
|
+
scan.ExportAssignment = (s: State, node: ts.ExportAssignment) => {
|
|
170
|
+
const exp = statement(s, node, 'export', () => ({ names: [], star: false }))
|
|
171
|
+
s.exports.push(exp)
|
|
172
|
+
s.exportsForm.set(exp.id, 'assignment')
|
|
173
|
+
s.exportsOrigin.set(exp.id, node)
|
|
174
|
+
return exp
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ---------------- Type Components ----------------
|
|
178
|
+
scan.Type = (s: State, node: ts.TypeNode): T.Type => {
|
|
179
|
+
if (ts.isLiteralTypeNode(node)) return scan.Literal(s, node)
|
|
180
|
+
if (ts.isArrayTypeNode(node)) return scan.Array(s, node)
|
|
181
|
+
if (ts.isTupleTypeNode(node)) return scan.Tuple(s, node)
|
|
182
|
+
if (ts.isUnionTypeNode(node)) return scan.Union(s, node)
|
|
183
|
+
if (ts.isIntersectionTypeNode(node)) return scan.Intersection(s, node)
|
|
184
|
+
if (ts.isTypeOperatorNode(node)) return scan.TypeOperator(s, node)
|
|
185
|
+
if (ts.isFunctionTypeNode(node) || ts.isConstructorTypeNode(node)) return scan.FunctionType(s, node)
|
|
186
|
+
if (ts.isTypeLiteralNode(node)) return scan.Record(s, node)
|
|
187
|
+
if (ts.isParenthesizedTypeNode(node)) return scan.Type(s, node.type)
|
|
188
|
+
if (ts.isConditionalTypeNode(node)) return scan.Conditional(s, node)
|
|
189
|
+
if (ts.isInferTypeNode(node)) return scan.Infer(s, node)
|
|
190
|
+
if (ts.isIndexedAccessTypeNode(node)) return scan.IndexedAccess(s, node)
|
|
191
|
+
if (ts.isMappedTypeNode(node)) return scan.Mapped(s, node)
|
|
192
|
+
if (ts.isTypeQueryNode(node)) return scan.Query(s, node)
|
|
193
|
+
if (ts.isTemplateLiteralTypeNode(node)) return scan.TemplateLiteral(s, node)
|
|
194
|
+
if (ts.isTypePredicateNode(node)) return scan.Predicate(s, node)
|
|
195
|
+
if (ts.isImportTypeNode(node)) return scan.ImportType(s, node)
|
|
196
|
+
const name = INTRINSICS[node.kind]
|
|
197
|
+
if (name) return scan.Intrinsic(s, node, name)
|
|
198
|
+
|
|
199
|
+
if (ts.isTypeReferenceNode(node)) return scan.TypeReference(s, node)
|
|
200
|
+
|
|
201
|
+
return scan.Unknown(s, node)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
scan.Literal = (s: State, node: ts.LiteralTypeNode): T.Type<'literal'> =>
|
|
205
|
+
type(s, node, 'literal', { value: literalValue(node.literal) })
|
|
206
|
+
|
|
207
|
+
scan.Array = (s: State, node: ts.ArrayTypeNode): T.Type<'array'> =>
|
|
208
|
+
type(s, node, 'array', { elementType: scan.Type(s, node.elementType) })
|
|
209
|
+
|
|
210
|
+
scan.Union = (s: State, node: ts.UnionTypeNode): T.Type<'union'> =>
|
|
211
|
+
type(s, node, 'union', { types: node.types.map((t) => scan.Type(s, t)) })
|
|
212
|
+
|
|
213
|
+
scan.Intersection = (s: State, node: ts.IntersectionTypeNode): T.Type<'intersection'> =>
|
|
214
|
+
type(s, node, 'intersection', { types: node.types.map((t) => scan.Type(s, t)) })
|
|
215
|
+
|
|
216
|
+
scan.Tuple = (s: State, node: ts.TupleTypeNode): T.Type<'tuple'> =>
|
|
217
|
+
type(s, node, 'tuple', { elements: node.elements.map((el) => tupleElement(s, el)) })
|
|
218
|
+
|
|
219
|
+
scan.TypeOperator = (s: State, node: ts.TypeOperatorNode): T.Type<'type-operator'> =>
|
|
220
|
+
type(s, node, 'type-operator', { operator: TYPE_OPERATORS[node.operator]!, target: scan.Type(s, node.type) })
|
|
221
|
+
|
|
222
|
+
scan.FunctionType = (s: State, node: ts.FunctionTypeNode | ts.ConstructorTypeNode): T.Type<'function-type'> =>
|
|
223
|
+
type(s, node, 'function-type', { signatures: [signature(s, node)] })
|
|
224
|
+
|
|
225
|
+
scan.Record = (s: State, node: ts.TypeLiteralNode): T.Type<'record'> =>
|
|
226
|
+
type(s, node, 'record', objectMembers(s, node.members))
|
|
227
|
+
|
|
228
|
+
scan.Conditional = (s: State, node: ts.ConditionalTypeNode): T.Type<'conditional'> =>
|
|
229
|
+
type(s, node, 'conditional', {
|
|
230
|
+
check: scan.Type(s, node.checkType),
|
|
231
|
+
extends: scan.Type(s, node.extendsType),
|
|
232
|
+
true: scan.Type(s, node.trueType),
|
|
233
|
+
false: scan.Type(s, node.falseType),
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
scan.Infer = (s: State, node: ts.InferTypeNode): T.Type<'infer'> =>
|
|
237
|
+
type(s, node, 'infer', {
|
|
238
|
+
name: node.typeParameter.name.text,
|
|
239
|
+
...(node.typeParameter.constraint ? { constraint: scan.Type(s, node.typeParameter.constraint) } : {}),
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
scan.IndexedAccess = (s: State, node: ts.IndexedAccessTypeNode): T.Type<'indexed-access'> =>
|
|
243
|
+
type(s, node, 'indexed-access', { object: scan.Type(s, node.objectType), index: scan.Type(s, node.indexType) })
|
|
244
|
+
|
|
245
|
+
scan.Mapped = (s: State, node: ts.MappedTypeNode): T.Type<'mapped'> =>
|
|
246
|
+
type(s, node, 'mapped', {
|
|
247
|
+
typeParameter: scan.TypeParam(s, node.typeParameter),
|
|
248
|
+
...(node.nameType ? { nameType: scan.Type(s, node.nameType) } : {}),
|
|
249
|
+
...(node.type ? { type: scan.Type(s, node.type) } : {}),
|
|
250
|
+
...(node.questionToken ? { optional: true } : {}),
|
|
251
|
+
...(node.readonlyToken ? { readonly: true } : {}),
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
scan.Query = (s: State, node: ts.TypeQueryNode): T.Type<'query'> =>
|
|
255
|
+
type(s, node, 'query', {
|
|
256
|
+
name: node.exprName.getText(),
|
|
257
|
+
...(node.typeArguments?.length ? { args: node.typeArguments.map((a) => scan.Type(s, a)) } : {}),
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
scan.TemplateLiteral = (s: State, node: ts.TemplateLiteralTypeNode): T.Type<'template-literal'> =>
|
|
261
|
+
type(s, node, 'template-literal', {
|
|
262
|
+
head: node.head.text,
|
|
263
|
+
spans: node.templateSpans.map((sp) => ({ type: scan.Type(s, sp.type), literal: sp.literal.text })),
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
scan.Predicate = (s: State, node: ts.TypePredicateNode): T.Type<'predicate'> =>
|
|
267
|
+
type(s, node, 'predicate', {
|
|
268
|
+
parameter: node.parameterName.getText(),
|
|
269
|
+
...(node.assertsModifier ? { asserts: true } : {}),
|
|
270
|
+
...(node.type ? { type: scan.Type(s, node.type) } : {}),
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
scan.ImportType = (s: State, node: ts.ImportTypeNode): T.Type<'import-type'> => {
|
|
274
|
+
const arg = ts.isLiteralTypeNode(node.argument) && ts.isStringLiteral(node.argument.literal)
|
|
275
|
+
? node.argument.literal.text
|
|
276
|
+
: node.argument.getText()
|
|
277
|
+
return type(s, node, 'import-type', {
|
|
278
|
+
argument: arg,
|
|
279
|
+
...(node.qualifier ? { qualifier: node.qualifier.getText() } : {}),
|
|
280
|
+
...(node.isTypeOf ? { isTypeOf: true } : {}),
|
|
281
|
+
...(node.typeArguments?.length ? { args: node.typeArguments.map((a) => scan.Type(s, a)) } : {}),
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
scan.TypeReference = (s: State, node: ts.TypeReferenceNode): T.Type<'reference'> => {
|
|
286
|
+
const r = type(s, node, 'reference', { type: 'internal', targetId: 0 } as any)
|
|
287
|
+
r.id = s.nextId()
|
|
288
|
+
r.owner = s.currentStmt
|
|
289
|
+
r.name = getName(node) ?? 'unknown'
|
|
290
|
+
if (node.typeArguments?.length) r.args = node.typeArguments.map((a) => scan.Type(s, a))
|
|
291
|
+
s.references.push(r)
|
|
292
|
+
s.referenceOrigins.set(r.id, node)
|
|
293
|
+
return r
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
scan.Unknown = (s: State, node: ts.Node): T.Type<'unknown'> =>
|
|
297
|
+
type(s, node, 'unknown', { text: node.getText(), nodeType: ts.SyntaxKind[node.kind] })
|
|
298
|
+
|
|
299
|
+
scan.Intrinsic = (s: State, node: ts.Node, name: T.IntrinsicName): T.Type => {
|
|
300
|
+
return type(s, node, 'intrinsic', { name })
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
scan.TypeParam = (s: State, node: ts.TypeParameterDeclaration): T.Part<'generic'> => {
|
|
304
|
+
return part(s, node, 'generic', {
|
|
305
|
+
constraint: node.constraint ? scan.Type(s, node.constraint) : undefined,
|
|
306
|
+
default: node.default ? scan.Type(s, node.default) : undefined,
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ---------------- Inference ----------------
|
|
311
|
+
/**
|
|
312
|
+
* Infer a declaration's type from the checker when no annotation is present.
|
|
313
|
+
* Builds a structured `Type` for the common shapes (primitives, literals,
|
|
314
|
+
* unions, arrays, named references, object literals) and falls back to the
|
|
315
|
+
* checker's string form for anything more exotic — the hybrid contract.
|
|
316
|
+
*/
|
|
317
|
+
const inferAt = (s: State, node: ts.Node): T.Type => fromType(s, node, s.checker.getTypeAtLocation(node), new Set())
|
|
318
|
+
|
|
319
|
+
const inferReturn = (s: State, node: ts.SignatureDeclarationBase): T.Type => {
|
|
320
|
+
const sig = s.checker.getSignatureFromDeclaration(node as ts.SignatureDeclaration)
|
|
321
|
+
return sig ? fromType(s, node, sig.getReturnType(), new Set()) : scan.Intrinsic(s, node, 'unknown')
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const fromType = (s: State, ctx: ts.Node, type: ts.Type, seen: Set<ts.Type>): T.Type =>
|
|
325
|
+
structured(s, ctx, type, seen) ?? inferredText(s, ctx, type)
|
|
326
|
+
|
|
327
|
+
const inferredText = (s: State, ctx: ts.Node, type: ts.Type): T.Type =>
|
|
328
|
+
inode(s, 'unknown', { text: s.checker.typeToString(type, ctx, ts.TypeFormatFlags.NoTruncation), nodeType: 'inferred' })
|
|
329
|
+
|
|
330
|
+
const structured = (s: State, ctx: ts.Node, type: ts.Type, seen: Set<ts.Type>): T.Type | undefined => {
|
|
331
|
+
const f = type.flags
|
|
332
|
+
// Literals first — their flags never overlap the primitive intrinsics.
|
|
333
|
+
if (f & ts.TypeFlags.StringLiteral) return inode(s, 'literal', { value: (type as ts.StringLiteralType).value })
|
|
334
|
+
if (f & ts.TypeFlags.NumberLiteral) return inode(s, 'literal', { value: (type as ts.NumberLiteralType).value })
|
|
335
|
+
if (f & ts.TypeFlags.BigIntLiteral) {
|
|
336
|
+
const v = (type as ts.BigIntLiteralType).value
|
|
337
|
+
return inode(s, 'literal', { value: BigInt((v.negative ? '-' : '') + v.base10Value) })
|
|
338
|
+
}
|
|
339
|
+
if (f & ts.TypeFlags.BooleanLiteral) return inode(s, 'literal', { value: (type as any).intrinsicName === 'true' })
|
|
340
|
+
const intr = intrinsicName(f)
|
|
341
|
+
if (intr) return inode(s, 'intrinsic', { name: intr })
|
|
342
|
+
// Keep a named alias rather than expanding it inline.
|
|
343
|
+
const alias = type.aliasSymbol
|
|
344
|
+
if (alias && isNamed(alias)) return inferRef(s, alias.getName(), alias, mapArgs(s, ctx, type.aliasTypeArguments, seen))
|
|
345
|
+
if (type.isUnion()) return inode(s, 'union', { types: type.types.map((t) => fromType(s, ctx, t, seen)) })
|
|
346
|
+
if (type.isIntersection()) return inode(s, 'intersection', { types: type.types.map((t) => fromType(s, ctx, t, seen)) })
|
|
347
|
+
return objectType(s, ctx, type, seen)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** Arrays, named references, and anonymous object literals. */
|
|
351
|
+
const objectType = (s: State, ctx: ts.Node, type: ts.Type, seen: Set<ts.Type>): T.Type | undefined => {
|
|
352
|
+
if (!(type.flags & ts.TypeFlags.Object)) return undefined
|
|
353
|
+
const obj = type as ts.ObjectType
|
|
354
|
+
if (obj.objectFlags & ts.ObjectFlags.Reference) {
|
|
355
|
+
const ref = type as ts.TypeReference
|
|
356
|
+
if (ref.target.objectFlags & ts.ObjectFlags.Tuple) return undefined // → string form
|
|
357
|
+
const args = s.checker.getTypeArguments(ref)
|
|
358
|
+
const tname = ref.target.symbol?.getName()
|
|
359
|
+
if ((tname === 'Array' || tname === 'ReadonlyArray') && args.length === 1)
|
|
360
|
+
return inode(s, 'array', { elementType: fromType(s, ctx, args[0]!, seen) })
|
|
361
|
+
const sym = type.getSymbol()
|
|
362
|
+
if (sym && isNamed(sym)) return inferRef(s, sym.getName(), sym, mapArgs(s, ctx, args, seen))
|
|
363
|
+
}
|
|
364
|
+
const sym = type.getSymbol()
|
|
365
|
+
if (sym && isNamed(sym)) return inferRef(s, sym.getName(), sym, undefined)
|
|
366
|
+
// Callable / constructable objects are too rich for a record — defer to text.
|
|
367
|
+
if (type.getCallSignatures().length || type.getConstructSignatures().length) return undefined
|
|
368
|
+
const props = type.getProperties()
|
|
369
|
+
if (!props.length || seen.has(type)) return undefined
|
|
370
|
+
seen.add(type)
|
|
371
|
+
return inode(s, 'record', { properties: props.map((p) => inferProp(s, ctx, p, seen)), methods: [] })
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const inferProp = (s: State, ctx: ts.Node, sym: ts.Symbol, seen: Set<ts.Type>): T.Part<'property'> => {
|
|
375
|
+
const decl = sym.valueDeclaration ?? sym.declarations?.[0] ?? ctx
|
|
376
|
+
const pt = s.checker.getTypeOfSymbolAtLocation(sym, decl)
|
|
377
|
+
return {
|
|
378
|
+
kind: 'property',
|
|
379
|
+
parent: s.parent,
|
|
380
|
+
sources: [],
|
|
381
|
+
name: sym.getName(),
|
|
382
|
+
type: fromType(s, ctx, pt, seen),
|
|
383
|
+
...(sym.flags & ts.SymbolFlags.Optional ? { optional: true } : {}),
|
|
384
|
+
} as T.Part<'property'>
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const inferRef = (s: State, name: string, symbol: ts.Symbol, args?: T.Type[]): T.Type<'reference'> => {
|
|
388
|
+
const r = {
|
|
389
|
+
kind: 'reference',
|
|
390
|
+
parent: s.parent,
|
|
391
|
+
sources: [],
|
|
392
|
+
type: 'internal',
|
|
393
|
+
targetId: 0,
|
|
394
|
+
id: s.nextId(),
|
|
395
|
+
name,
|
|
396
|
+
owner: s.currentStmt,
|
|
397
|
+
...(args?.length ? { args } : {}),
|
|
398
|
+
} as T.Type<'reference'>
|
|
399
|
+
s.references.push(r)
|
|
400
|
+
s.referenceSymbols.set(r.id, symbol)
|
|
401
|
+
return r
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const mapArgs = (s: State, ctx: ts.Node, args: readonly ts.Type[] | undefined, seen: Set<ts.Type>): T.Type[] | undefined =>
|
|
405
|
+
args?.length ? args.map((a) => fromType(s, ctx, a, seen)) : undefined
|
|
406
|
+
|
|
407
|
+
/** A symbol that should render as a clickable name rather than an expanded shape. */
|
|
408
|
+
const isNamed = (sym: ts.Symbol): boolean => {
|
|
409
|
+
if (sym.flags & ts.SymbolFlags.TypeParameter) return false
|
|
410
|
+
const n = sym.getName()
|
|
411
|
+
return !!n && !n.startsWith('__')
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const inode = <K extends keyof T.TypeMap>(
|
|
415
|
+
s: State,
|
|
416
|
+
kind: K,
|
|
417
|
+
fields: Omit<T.TypeMap[K], keyof T.Typebase | 'kind'>,
|
|
418
|
+
): T.Type<K> => ({ kind, parent: s.parent, sources: [], ...fields }) as unknown as T.Type<K>
|
|
419
|
+
|
|
420
|
+
const intrinsicName = (f: ts.TypeFlags): T.IntrinsicName | undefined => {
|
|
421
|
+
if (f & ts.TypeFlags.String) return 'string'
|
|
422
|
+
if (f & ts.TypeFlags.Number) return 'number'
|
|
423
|
+
if (f & ts.TypeFlags.Boolean) return 'boolean'
|
|
424
|
+
if (f & ts.TypeFlags.BigInt) return 'bigint'
|
|
425
|
+
if (f & (ts.TypeFlags.ESSymbol | ts.TypeFlags.UniqueESSymbol)) return 'symbol'
|
|
426
|
+
if (f & ts.TypeFlags.Void) return 'void'
|
|
427
|
+
if (f & ts.TypeFlags.Undefined) return 'undefined'
|
|
428
|
+
if (f & ts.TypeFlags.Null) return 'null'
|
|
429
|
+
if (f & ts.TypeFlags.Never) return 'never'
|
|
430
|
+
if (f & ts.TypeFlags.Any) return 'any'
|
|
431
|
+
if (f & ts.TypeFlags.Unknown) return 'unknown'
|
|
432
|
+
if (f & ts.TypeFlags.NonPrimitive) return 'object'
|
|
433
|
+
return undefined
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// ---------------- Type Components ----------------
|
|
437
|
+
const signature = (s: State, node: ts.SignatureDeclarationBase): T.Part<'signature'> =>
|
|
438
|
+
part(s, node, 'signature', {
|
|
439
|
+
...(node.typeParameters ? { generics: node.typeParameters.map((tp) => scan.TypeParam(s, tp)) } : {}),
|
|
440
|
+
params: node.parameters.map((p) => parameter(s, p)),
|
|
441
|
+
return: node.type ? scan.Type(s, node.type) : inferReturn(s, node),
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
const parameter = (b: State, node: ts.ParameterDeclaration): T.Part<'parameter'> =>
|
|
445
|
+
part(b, node, 'parameter', {
|
|
446
|
+
type: node.type ? scan.Type(b, node.type) : inferAt(b, node),
|
|
447
|
+
optional: !!node.questionToken || !!node.initializer,
|
|
448
|
+
...(node.dotDotDotToken ? { rest: true } : {}),
|
|
449
|
+
...(node.initializer ? { default: node.initializer.getText() } : {}),
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
const functionBody = (s: State, node: ts.SignatureDeclarationBase): T.DeclerationDefinitions['function'] => ({
|
|
453
|
+
signatures: [signature(s, node)],
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
const commentForNode = (b: State, node: ts.Node): T.Comment | undefined => {
|
|
457
|
+
const all = ts.getJSDocCommentsAndTags(node)
|
|
458
|
+
if (!all.length) return undefined
|
|
459
|
+
const parts: T.CommentPart[] = []
|
|
460
|
+
const tags: T.CommentTag[] = []
|
|
461
|
+
let seenBlock = false
|
|
462
|
+
for (const doc of all) {
|
|
463
|
+
if (!ts.isJSDoc(doc)) continue
|
|
464
|
+
seenBlock = true
|
|
465
|
+
appendCommentBody(doc.comment, parts)
|
|
466
|
+
if (doc.tags)
|
|
467
|
+
for (const t of doc.tags) {
|
|
468
|
+
const tag = buildTag(b, t)
|
|
469
|
+
if (tag.tag === '@module') return undefined
|
|
470
|
+
tags.push(tag)
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (!seenBlock) return undefined
|
|
474
|
+
return { parts, ...(tags.length ? { tags } : {}) }
|
|
475
|
+
}
|
|
476
|
+
/** Flatten a JSDoc comment into `parts`. */
|
|
477
|
+
const appendCommentBody = (
|
|
478
|
+
comment: string | ts.NodeArray<ts.JSDocComment> | undefined,
|
|
479
|
+
parts: T.CommentPart[],
|
|
480
|
+
): void => {
|
|
481
|
+
if (!comment) return
|
|
482
|
+
if (typeof comment === 'string') {
|
|
483
|
+
const trimmed = comment.trim()
|
|
484
|
+
if (trimmed) parts.push({ kind: 'text', text: trimmed })
|
|
485
|
+
return
|
|
486
|
+
}
|
|
487
|
+
for (const c of comment) {
|
|
488
|
+
if (c.kind === ts.SyntaxKind.JSDocText) {
|
|
489
|
+
parts.push({ kind: 'text', text: c.text })
|
|
490
|
+
continue
|
|
491
|
+
}
|
|
492
|
+
const target = c.name?.getText() ?? ''
|
|
493
|
+
const linkText = c.text || undefined
|
|
494
|
+
const style = ts.isJSDocLinkCode(c) ? ('code' as const) : ts.isJSDocLinkPlain(c) ? ('plain' as const) : undefined
|
|
495
|
+
parts.push({ kind: 'link', target, ...(linkText ? { text: linkText } : {}), ...(style ? { style } : {}) })
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
const buildTag = (s: State, tag: ts.JSDocTag): T.CommentTag => {
|
|
499
|
+
const text = ts.getTextOfJSDocComment(tag.comment)?.trim() ?? ''
|
|
500
|
+
const exprType = (te?: ts.JSDocTypeExpression) => (te ? scan.Type(s, te.type) : undefined)
|
|
501
|
+
if (ts.isJSDocPropertyTag(tag)) {
|
|
502
|
+
return {
|
|
503
|
+
tag: '@property',
|
|
504
|
+
name: tag.name.getText(),
|
|
505
|
+
type: exprType(tag.typeExpression),
|
|
506
|
+
text,
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (ts.isJSDocParameterTag(tag)) {
|
|
510
|
+
return {
|
|
511
|
+
tag: '@param',
|
|
512
|
+
name: tag.name.getText(),
|
|
513
|
+
type: exprType(tag.typeExpression),
|
|
514
|
+
...(tag.isBracketed ? { optional: true } : {}),
|
|
515
|
+
text,
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
if (ts.isJSDocReturnTag(tag)) {
|
|
519
|
+
const type = exprType(tag.typeExpression)
|
|
520
|
+
return { tag: '@returns', ...(type ? { type } : {}), text }
|
|
521
|
+
}
|
|
522
|
+
if (ts.isJSDocThrowsTag(tag)) {
|
|
523
|
+
const type = exprType(tag.typeExpression)
|
|
524
|
+
return { tag: '@throws', ...(type ? { type } : {}), text }
|
|
525
|
+
}
|
|
526
|
+
if (ts.isJSDocTypeTag(tag)) return { tag: '@type', type: exprType(tag.typeExpression)!, text }
|
|
527
|
+
if (ts.isJSDocSatisfiesTag(tag)) return { tag: '@satisfies', type: exprType(tag.typeExpression)!, text }
|
|
528
|
+
if (ts.isJSDocTemplateTag(tag)) {
|
|
529
|
+
return { tag: '@template', generics: tag.typeParameters.map((tp) => scan.TypeParam(s, tp)), text }
|
|
530
|
+
}
|
|
531
|
+
if (ts.isJSDocSeeTag(tag)) {
|
|
532
|
+
return { tag: '@see', ...(tag.name ? { target: tag.name.name.getText() } : {}), text }
|
|
533
|
+
}
|
|
534
|
+
if (ts.isJSDocAugmentsTag(tag)) return { tag: '@augments', class: scan.Type(s, tag.class), text }
|
|
535
|
+
if (ts.isJSDocImplementsTag(tag)) return { tag: '@implements', class: scan.Type(s, tag.class), text }
|
|
536
|
+
const name = '@' + tag.tagName.text
|
|
537
|
+
// `@example` carries semantic indentation; re-extract from source so the
|
|
538
|
+
// leader-strip never eats author tabs (see `rawTagBody`).
|
|
539
|
+
if (name === '@example') return parseExample(rawTagBody(tag))
|
|
540
|
+
return { tag: name, text }
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Reconstruct the body of a JSDoc tag from source, stripping the per-line
|
|
544
|
+
* `*` leader and at most one *space* of separator. Unlike
|
|
545
|
+
* `ts.getTextOfJSDocComment` — which strips `[ \t]?` and so eats a single
|
|
546
|
+
* tab of user indentation — this preserves tabs intact.
|
|
547
|
+
*/
|
|
548
|
+
const rawTagBody = (tag: ts.JSDocTag): string => {
|
|
549
|
+
const src = tag.getSourceFile().text
|
|
550
|
+
// Body starts immediately after the tag name (`@example`), runs to tag end.
|
|
551
|
+
const raw = src.slice(tag.tagName.end, tag.end)
|
|
552
|
+
return raw
|
|
553
|
+
.split('\n')
|
|
554
|
+
.map((line, i) => (i === 0 ? line : line.replace(/^[ \t]*\*( ?)/, '')))
|
|
555
|
+
.join('\n')
|
|
556
|
+
.trim()
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Pull an optional caption out of an `@example` body. Two forms are recognised:
|
|
560
|
+
* 1. Legacy JSDoc: `<caption>…</caption>` prefix.
|
|
561
|
+
* 2. TypeDoc-style: any text on the line(s) before the first fenced code
|
|
562
|
+
* block becomes the caption; the fence and its body become the code.
|
|
563
|
+
* When neither pattern matches, the entire body is treated as `code`.
|
|
564
|
+
*/
|
|
565
|
+
const parseExample = (raw: string): T.CommentTagMap['@example'] => {
|
|
566
|
+
const html = raw.match(/^<caption>([\s\S]*?)<\/caption>\s*([\s\S]*)$/)
|
|
567
|
+
if (html) return { tag: '@example', caption: html[1]!.trim(), code: html[2]!.trim() }
|
|
568
|
+
const fence = raw.search(/^```/m)
|
|
569
|
+
if (fence > 0) {
|
|
570
|
+
const caption = raw.slice(0, fence).trim()
|
|
571
|
+
if (caption) return { tag: '@example', caption, code: raw.slice(fence).trim() }
|
|
572
|
+
}
|
|
573
|
+
return { tag: '@example', code: raw.trim() }
|
|
574
|
+
}
|
|
575
|
+
export const commentForModule = (s: State, sf: ts.SourceFile): T.Comment | undefined => {
|
|
576
|
+
if (sf.statements.length === 0) return undefined
|
|
577
|
+
// 1. Target the leading comment ranges for the first statement per your rules
|
|
578
|
+
const sourceText = sf.getFullText()
|
|
579
|
+
const commentRanges = ts.getLeadingCommentRanges(sourceText, sf.statements[0]!.pos)
|
|
580
|
+
if (!commentRanges || commentRanges.length === 0) return undefined
|
|
581
|
+
const parts: T.CommentPart[] = []
|
|
582
|
+
const tags: T.CommentTag[] = []
|
|
583
|
+
let seenBlock = false
|
|
584
|
+
// 2. Evaluate each comment text range
|
|
585
|
+
for (let i = 0; i < commentRanges.length; i++) {
|
|
586
|
+
const range = commentRanges[i]!
|
|
587
|
+
const commentText = sourceText.slice(range.pos, range.end)
|
|
588
|
+
// Apply your specific layout logic
|
|
589
|
+
const isModuleComment = i < commentRanges.length - 1 || commentText.includes('@module')
|
|
590
|
+
if (isModuleComment) {
|
|
591
|
+
// 3. Trick the compiler into parsing the raw text snippet back into an AST Node block
|
|
592
|
+
// We append an empty statement (;) so the parser attaches the JSDoc to a valid target.
|
|
593
|
+
const dummyFile = ts.createSourceFile(
|
|
594
|
+
'dummy.ts',
|
|
595
|
+
`${commentText}\n;`,
|
|
596
|
+
ts.ScriptTarget.Latest,
|
|
597
|
+
true, // Set parent pointers to true
|
|
598
|
+
)
|
|
599
|
+
// 4. Safely extract the compiled JSDoc node from the dummy file
|
|
600
|
+
const dummyStmt = dummyFile.statements[0]
|
|
601
|
+
if (dummyStmt) {
|
|
602
|
+
const jsdocBlocks = ts.getJSDocCommentsAndTags(dummyStmt)
|
|
603
|
+
const matchingBlock = jsdocBlocks.find(ts.isJSDoc) as ts.JSDoc | undefined
|
|
604
|
+
if (matchingBlock) {
|
|
605
|
+
seenBlock = true
|
|
606
|
+
// Feed the generated AST structure down your pipeline seamlessly
|
|
607
|
+
appendCommentBody(matchingBlock.comment, parts)
|
|
608
|
+
if (matchingBlock.tags) {
|
|
609
|
+
for (const t of matchingBlock.tags) {
|
|
610
|
+
tags.push(buildTag(s, t))
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (!seenBlock) return undefined
|
|
618
|
+
return { parts, ...(tags.length ? { tags } : {}) }
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const statement = <K extends keyof T.DeclarationMap>(
|
|
622
|
+
s: State,
|
|
623
|
+
node: ts.Node,
|
|
624
|
+
kind: K,
|
|
625
|
+
fields: () => Omit<T.DeclarationMap[K], keyof T.Base | 'kind'> & Partial<T.Base>,
|
|
626
|
+
): T.Declaration<K> => {
|
|
627
|
+
const b = base(s, node)
|
|
628
|
+
s.currentStmt = b.id
|
|
629
|
+
Object.assign(b, fields(), { kind })
|
|
630
|
+
s.declarations.push(b as any)
|
|
631
|
+
return b as any
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const type = <K extends keyof T.TypeMap>(
|
|
635
|
+
s: State,
|
|
636
|
+
node: ts.Node,
|
|
637
|
+
kind: K,
|
|
638
|
+
fields: Omit<T.TypeMap[K], keyof T.Base | 'kind'> & Partial<T.Base>,
|
|
639
|
+
): T.Type<K> => {
|
|
640
|
+
const nd = typeBase(s, node) as T.Type
|
|
641
|
+
Object.assign(nd, { kind }, fields)
|
|
642
|
+
return nd as any
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const part = <K extends keyof T.PartMap>(
|
|
646
|
+
s: State,
|
|
647
|
+
node: ts.Node,
|
|
648
|
+
kind: K,
|
|
649
|
+
fields: Omit<T.PartMap[K], 'kind' | 'name' | keyof T.Typebase> & { name?: string },
|
|
650
|
+
): T.Part<K> => {
|
|
651
|
+
const nd = typeBase(s, node) as T.Typebase & { kind?: string; name?: string }
|
|
652
|
+
Object.assign(nd, { kind }, fields)
|
|
653
|
+
if (nd.name === undefined) {
|
|
654
|
+
const n = getName(node)
|
|
655
|
+
if (n !== undefined) nd.name = n
|
|
656
|
+
}
|
|
657
|
+
return nd as any
|
|
658
|
+
}
|
|
659
|
+
const base = (s: State, node: ts.Node): T.Base => {
|
|
660
|
+
const result: T.Base = typeBase(s, node) as any
|
|
661
|
+
result.id = s.nextId()
|
|
662
|
+
result.name = getName(node) ?? 'unknown'
|
|
663
|
+
result.exported = isExported(node)
|
|
664
|
+
|
|
665
|
+
const named = (node as { name?: ts.Node }).name
|
|
666
|
+
const sym = s.checker.getSymbolAtLocation(named ?? node)
|
|
667
|
+
if (sym) s.symbolsById.set(result.id, sym)
|
|
668
|
+
|
|
669
|
+
return result
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const typeBase = (s: State, node: ts.Node): T.Typebase => {
|
|
673
|
+
const result: T.Typebase = { parent: s.parent, sources: [] } as T.Typebase
|
|
674
|
+
|
|
675
|
+
const named = (node as { name?: ts.Node }).name
|
|
676
|
+
const sym = s.checker.getSymbolAtLocation(named ?? node)
|
|
677
|
+
if (sym?.declarations?.length) result.sources = sym.declarations!.map((d) => sourceOf(s, d))
|
|
678
|
+
else result.sources = [sourceOf(s, node)]
|
|
679
|
+
|
|
680
|
+
const comment = ts.isSourceFile(node) ? commentForModule(s, node) : commentForNode(s, node)
|
|
681
|
+
if (comment) result.comment = comment
|
|
682
|
+
|
|
683
|
+
return result
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const sourceOf = (s: State, node: ts.Node): T.Source => {
|
|
687
|
+
const sf = node.getSourceFile()
|
|
688
|
+
const { line, character } = sf.getLineAndCharacterOfPosition(node.getStart())
|
|
689
|
+
return { file: s.getPath(sf), line: line + 1, column: character + 1 }
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// ---------------- Utilities ----------------
|
|
693
|
+
export const isExported = (node: ts.Node): boolean => {
|
|
694
|
+
if (ts.isExportDeclaration(node) || ts.isExportAssignment(node)) return true
|
|
695
|
+
// `export const x` carries the modifier on the enclosing `VariableStatement`.
|
|
696
|
+
if (ts.isVariableDeclaration(node)) {
|
|
697
|
+
const stmt = node.parent?.parent
|
|
698
|
+
return !!stmt && ts.isVariableStatement(stmt) && isExported(stmt)
|
|
699
|
+
}
|
|
700
|
+
const mods = (node as { modifiers?: ts.NodeArray<ts.ModifierLike> }).modifiers
|
|
701
|
+
return !!mods?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
export const getName = (node: ts.Node): string | undefined => {
|
|
705
|
+
if (ts.isTypeReferenceNode(node)) return node.typeName.getText()
|
|
706
|
+
if (ts.isExpressionWithTypeArguments(node)) return node.expression.getText()
|
|
707
|
+
if (ts.isTypeQueryNode(node)) return node.exprName.getText()
|
|
708
|
+
if (ts.isDeclarationStatement(node)) return ts.getNameOfDeclaration(node)?.getText()
|
|
709
|
+
if (ts.isExpression(node)) return ts.getNameOfDeclaration(node)?.getText()
|
|
710
|
+
if ((node as { name?: ts.Node }).name) return (node as { name?: ts.Node }).name!.getText()
|
|
711
|
+
return undefined
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const property = (s: State, node: ts.PropertyDeclaration | ts.PropertySignature): T.Part<'property'> =>
|
|
715
|
+
part(s, node, 'property', {
|
|
716
|
+
type: node.type ? scan.Type(s, node.type) : inferAt(s, node),
|
|
717
|
+
...(node.questionToken ? { optional: true } : {}),
|
|
718
|
+
...('initializer' in node && node.initializer ? { defaultValue: node.initializer.getText() } : {}),
|
|
719
|
+
})
|
|
720
|
+
|
|
721
|
+
const method = (s: State, node: ts.MethodDeclaration | ts.MethodSignature): T.Part<'method'> =>
|
|
722
|
+
part(s, node, 'method', { signatures: [signature(s, node)] })
|
|
723
|
+
|
|
724
|
+
const indexSignatureDecl = (s: State, node: ts.IndexSignatureDeclaration): T.Part<'index-signature'> =>
|
|
725
|
+
part(s, node, 'index-signature', {
|
|
726
|
+
parameter: parameter(s, node.parameters[0]!),
|
|
727
|
+
type: node.type ? scan.Type(s, node.type) : scan.Intrinsic(s, node, 'unknown'),
|
|
728
|
+
})
|
|
729
|
+
|
|
730
|
+
const enumMember = (s: State, node: ts.EnumMember): T.Part<'enum-member'> => {
|
|
731
|
+
const value = s.checker.getConstantValue(node)
|
|
732
|
+
return part(s, node, 'enum-member', { ...(value !== undefined ? { value } : {}) })
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const generics = (s: State, node: { typeParameters?: ts.NodeArray<ts.TypeParameterDeclaration> }) =>
|
|
736
|
+
node.typeParameters?.length ? { generics: node.typeParameters.map((tp) => scan.TypeParam(s, tp)) } : {}
|
|
737
|
+
|
|
738
|
+
const heritage = (s: State, node: ts.ClassDeclaration): { extends?: T.Type[]; implements?: T.Type[] } => {
|
|
739
|
+
const out: { extends?: T.Type[]; implements?: T.Type[] } = {}
|
|
740
|
+
for (const h of node.heritageClauses ?? []) {
|
|
741
|
+
const types = h.types.map((t) => scan.Type(s, t))
|
|
742
|
+
if (h.token === ts.SyntaxKind.ExtendsKeyword) out.extends = types
|
|
743
|
+
else out.implements = types
|
|
744
|
+
}
|
|
745
|
+
return out
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const interfaceExtends = (s: State, node: ts.InterfaceDeclaration): { extends?: T.Type[] } => {
|
|
749
|
+
const ext = node.heritageClauses?.flatMap((h) => h.types).map((t) => scan.Type(s, t))
|
|
750
|
+
return ext?.length ? { extends: ext } : {}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const objectMembers = (s: State, members: ts.NodeArray<ts.TypeElement>) => {
|
|
754
|
+
const properties: T.Part<'property'>[] = []
|
|
755
|
+
const methods: T.Part<'method'>[] = []
|
|
756
|
+
const callSignatures: T.Part<'signature'>[] = []
|
|
757
|
+
const constructSignatures: T.Part<'signature'>[] = []
|
|
758
|
+
let indexSignature: T.Part<'index-signature'> | undefined
|
|
759
|
+
for (const m of members) {
|
|
760
|
+
if (ts.isPropertySignature(m) && ts.isIdentifier(m.name)) properties.push(property(s, m))
|
|
761
|
+
else if (ts.isMethodSignature(m) && ts.isIdentifier(m.name)) methods.push(method(s, m))
|
|
762
|
+
else if (ts.isCallSignatureDeclaration(m)) callSignatures.push(signature(s, m))
|
|
763
|
+
else if (ts.isConstructSignatureDeclaration(m)) constructSignatures.push(signature(s, m))
|
|
764
|
+
else if (ts.isIndexSignatureDeclaration(m)) indexSignature = indexSignatureDecl(s, m)
|
|
765
|
+
}
|
|
766
|
+
return {
|
|
767
|
+
properties,
|
|
768
|
+
methods,
|
|
769
|
+
...(callSignatures.length ? { callSignatures } : {}),
|
|
770
|
+
...(constructSignatures.length ? { constructSignatures } : {}),
|
|
771
|
+
...(indexSignature ? { indexSignature } : {}),
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
const literalValue = (lit: ts.Node): T.Type<'literal'>['value'] => {
|
|
776
|
+
if (lit.kind === ts.SyntaxKind.NullKeyword) return null
|
|
777
|
+
if (ts.isStringLiteral(lit)) return lit.text
|
|
778
|
+
if (ts.isNumericLiteral(lit)) return Number(lit.text)
|
|
779
|
+
if (lit.kind === ts.SyntaxKind.TrueKeyword) return true
|
|
780
|
+
if (lit.kind === ts.SyntaxKind.FalseKeyword) return false
|
|
781
|
+
if (ts.isBigIntLiteral(lit)) return BigInt(lit.text.replace(/n$/, ''))
|
|
782
|
+
return lit.getText()
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
const tupleElement = (s: State, el: ts.TypeNode): T.Part<'tuple-element'> => {
|
|
786
|
+
if (ts.isNamedTupleMember(el))
|
|
787
|
+
return part(s, el, 'tuple-element', {
|
|
788
|
+
type: scan.Type(s, el.type),
|
|
789
|
+
...(el.questionToken ? { optional: true } : {}),
|
|
790
|
+
...(el.dotDotDotToken ? { rest: true } : {}),
|
|
791
|
+
})
|
|
792
|
+
if (ts.isOptionalTypeNode(el)) return part(s, el, 'tuple-element', { type: scan.Type(s, el.type), optional: true })
|
|
793
|
+
if (ts.isRestTypeNode(el)) return part(s, el, 'tuple-element', { type: scan.Type(s, el.type), rest: true })
|
|
794
|
+
return part(s, el, 'tuple-element', { type: scan.Type(s, el) })
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const INTRINSICS: Partial<Record<ts.SyntaxKind, T.IntrinsicName>> = {
|
|
798
|
+
[ts.SyntaxKind.StringKeyword]: 'string',
|
|
799
|
+
[ts.SyntaxKind.NumberKeyword]: 'number',
|
|
800
|
+
[ts.SyntaxKind.BooleanKeyword]: 'boolean',
|
|
801
|
+
[ts.SyntaxKind.BigIntKeyword]: 'bigint',
|
|
802
|
+
[ts.SyntaxKind.SymbolKeyword]: 'symbol',
|
|
803
|
+
[ts.SyntaxKind.VoidKeyword]: 'void',
|
|
804
|
+
[ts.SyntaxKind.UndefinedKeyword]: 'undefined',
|
|
805
|
+
[ts.SyntaxKind.NeverKeyword]: 'never',
|
|
806
|
+
[ts.SyntaxKind.AnyKeyword]: 'any',
|
|
807
|
+
[ts.SyntaxKind.UnknownKeyword]: 'unknown',
|
|
808
|
+
[ts.SyntaxKind.ObjectKeyword]: 'object',
|
|
809
|
+
[ts.SyntaxKind.ThisType]: 'this',
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const TYPE_OPERATORS: Partial<Record<ts.SyntaxKind, 'keyof' | 'readonly' | 'unique'>> = {
|
|
813
|
+
[ts.SyntaxKind.KeyOfKeyword]: 'keyof',
|
|
814
|
+
[ts.SyntaxKind.ReadonlyKeyword]: 'readonly',
|
|
815
|
+
[ts.SyntaxKind.UniqueKeyword]: 'unique',
|
|
816
|
+
}
|