diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a27bfec --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "typedoc-plugin-sourcefile-url"] + path = typedoc-plugin-sourcefile-url + url = https://github.com/gdelmas/typedoc-plugin-sourcefile-url diff --git a/.vscode/settings.json b/.vscode/settings.json index 1e9908d..fa86a35 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,17 @@ { "prettier.enable": true, - "editor.formatOnSave": true -} + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + }, + "[javascriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + } +} \ No newline at end of file diff --git a/package.json b/package.json index 35fef0c..41f07f7 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "fs-extra": "^10.0.0", "got": "^11.8.2", "p-queue": "^7.1.0", + "pkg-up": "^3.1.0", "prettier": "^2.3.2", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -32,4 +33,4 @@ "singleQuote": true, "trailingComma": "all" } -} +} \ No newline at end of file diff --git a/src/unpkg-plugin/UnpkgPlugin.js b/src/unpkg-plugin/UnpkgPlugin.js new file mode 100644 index 0000000..6dee075 --- /dev/null +++ b/src/unpkg-plugin/UnpkgPlugin.js @@ -0,0 +1,13 @@ +require('ts-node').register({ + projectSearchDir: __dirname, +}); +const { UnpkgPlugin } = require('./UnpkgPluginImpl'); + +module.exports = function (PluginHost) { + var app = PluginHost.owner; + + // app.options.addDeclaration({name: 'sourcefile-url-map'}); + // app.options.addDeclaration({name: 'sourcefile-url-prefix'}); + + app.converter.addComponent('unpkg-plugin', UnpkgPlugin); +}; diff --git a/src/unpkg-plugin/UnpkgPluginImpl.ts b/src/unpkg-plugin/UnpkgPluginImpl.ts new file mode 100644 index 0000000..ab1c0f1 --- /dev/null +++ b/src/unpkg-plugin/UnpkgPluginImpl.ts @@ -0,0 +1,74 @@ +// Built by modifying https://github.com/gdelmas/typedoc-plugin-sourcefile-url + +import * as path from 'path'; +import * as fs from 'fs'; +import { Component } from 'typedoc/dist/lib/utils/component'; +import { ConverterComponent } from 'typedoc/dist/lib/converter/components'; +import { Converter } from 'typedoc/dist/lib/converter/converter'; +import { Context } from 'typedoc/dist/lib/converter/context'; +import { SourceReference } from 'typedoc/dist/lib/models/sources/file'; +import { Options } from 'typedoc/dist/lib/utils/options/options'; +import { sync as pkgUp } from 'pkg-up'; +import { URL } from 'url'; +import { dirname, relative } from 'path'; + +@Component({ name: 'unpkg-plugin' }) +export class UnpkgPlugin extends ConverterComponent { + private pkgUpCache = new Map(); + private pkgUp(cwd: string) { + let cacheEntry = this.pkgUpCache.get(cwd); + if (cacheEntry !== undefined) return cacheEntry; + cacheEntry = pkgUp({ cwd }); + this.pkgUpCache.set(cwd, cacheEntry); + return cacheEntry; + } + + public initialize(): void { + this.listenTo(this.owner, Converter.EVENT_BEGIN, this.onBegin); + } + + private onBegin(): void { + // register handler + this.listenTo(this.owner, Converter.EVENT_RESOLVE_END, this.onEndResolve); + } + + private onEndResolve(context: Context): void { + const project = context.project; + + // process mappings + for (const sourceFile of project.files) { + const { fullFileName } = sourceFile; + if (fullFileName) { + const pkgPath = this.pkgUp(dirname(fullFileName)); + if (pkgPath) { + const pkg = require(pkgPath); + const url = new URL('https://unpkg.com/'); + url.pathname = `browse/${pkg.name}@${pkg.version}/${relative( + dirname(pkgPath), + fullFileName, + )}`; + sourceFile.url = url.toString(); + // TODO should we include version number in this path? + // e.g. outdent@0.8.0/dist/index.d.ts instead of outdent/dist/index.d.ts? + sourceFile.fileName = relative(dirname(dirname(pkgPath)), fullFileName); + // TODO we are mutating the sourceFile.fileName solely so we can copy the value below. + // Should we avoid mutating sourceFile.fileName, *only* modifying `source.fileName` below? + } + } + } + + // add line anchors + for (let key in project.reflections) { + const reflection = project.reflections[key]; + + if (reflection.sources) { + reflection.sources.forEach((source: SourceReference) => { + if (source.file && source.file.url) { + source.url = source.file.url + '#L' + source.line; + source.fileName = source.file.fileName; + } + }); + } + } + } +} diff --git a/src/unpkg-plugin/package.json b/src/unpkg-plugin/package.json new file mode 100644 index 0000000..6a0d2ef --- /dev/null +++ b/src/unpkg-plugin/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} \ No newline at end of file diff --git a/src/unpkg-plugin/tsconfig.json b/src/unpkg-plugin/tsconfig.json new file mode 100644 index 0000000..d6205b0 --- /dev/null +++ b/src/unpkg-plugin/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "CommonJS" + } +} \ No newline at end of file diff --git a/src/work.ts b/src/work.ts index ee99183..e20db8b 100755 --- a/src/work.ts +++ b/src/work.ts @@ -102,6 +102,7 @@ for (const lib of libsToBuild) { entryPoints: [entrypointPath], out: outDir, externalPattern, + plugin: [resolve(fileURLToPath(import.meta.url), '../unpkg-plugin/UnpkgPlugin.js')], }, }, null, diff --git a/tsconfig.json b/tsconfig.json index 0a8bf1f..f21dc8b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,11 @@ { "extends": "@tsconfig/node16/tsconfig.json", - "include": ["src"], + "include": [ + "src" + ], + "exclude": [ + "src/unpkg-plugin" + ], "ts-node": { "transpileOnly": true // "transpiler": "ts-node/transpilers/swc-experimental" @@ -17,8 +22,11 @@ "isolatedModules": true, "strict": true, "moduleResolution": "node", - "types": ["node"], + "types": [ + "node" + ], "esModuleInterop": true, - "jsx": "react" + "jsx": "react", + "experimentalDecorators": true } -} +} \ No newline at end of file diff --git a/typedoc-plugin-sourcefile-url b/typedoc-plugin-sourcefile-url new file mode 160000 index 0000000..0e26a67 --- /dev/null +++ b/typedoc-plugin-sourcefile-url @@ -0,0 +1 @@ +Subproject commit 0e26a67b02479dbf69e3d2ba6a5d3f3d7fabe2f6 diff --git a/yarn.lock b/yarn.lock index 1e4dea5..533b702 100644 --- a/yarn.lock +++ b/yarn.lock @@ -366,6 +366,13 @@ eventemitter3@^4.0.7: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -511,6 +518,14 @@ keyv@^4.0.0: dependencies: json-buffer "3.0.1" +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + loose-envify@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -618,6 +633,20 @@ p-cancelable@^2.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + p-queue@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-7.1.0.tgz#c2bb28f8dc0ebf3fadb985b8706cf2ce5fe5f275" @@ -631,11 +660,28 @@ p-timeout@^5.0.0: resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-5.0.0.tgz#0f5dc08422e7243f8317669c461734cd1257a8dc" integrity sha512-z+bU/N7L1SABsqKnQzvAnINgPX7NHdzwUV+gHyJE7VGNDZSr03rhcPODCZSWiiT9k+gf74QPmzcZzqJRvxYZow== +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + prettier@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d"