Skip to content

Commit

Permalink
🐛 refactor: with monolithic source file
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfram77 committed Apr 14, 2023
1 parent 198a52b commit 36ee913
Show file tree
Hide file tree
Showing 17 changed files with 392 additions and 463 deletions.
30 changes: 15 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,21 @@ jobs:
- run: npm test


coverage:
name: Publish coverage
needs: [test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v2
with:
node-version: 18.x
- run: npm ci
- run: npm test
- uses: paambaati/[email protected]
- uses: coverallsapp/github-action@master
with:
github-token: ${{secrets.GITHUB_TOKEN}}
# coverage:
# name: Publish coverage
# needs: [test]
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: actions/setup-node@v2
# with:
# node-version: 18.x
# - run: npm ci
# - run: npm test
# - uses: paambaati/[email protected]
# - uses: coverallsapp/github-action@master
# with:
# github-token: ${{secrets.GITHUB_TOKEN}}


docs:
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,18 @@ asciinema.uploadSync('saved.cast');
<br>
<br>

[![](https://img.youtube.com/vi/rjDX5ItsOnQ/maxresdefault.jpg)](https://www.youtube.com/watch?v=rjDX5ItsOnQ)

## References

- [asciinema - Record and share your terminal sessions, the simple way](https://asciinema.org)

<br>
<br>


[![](https://img.youtube.com/vi/rjDX5ItsOnQ/maxresdefault.jpg)](https://www.youtube.com/watch?v=rjDX5ItsOnQ)<br>
[![ORG](https://img.shields.io/badge/org-nodef-green?logo=Org)](https://nodef.github.io)


[asciinema]: https://asciinema.org
[install asciinema first]: https://asciinema.org/docs/installation
Expand Down
291 changes: 101 additions & 190 deletions build.js
Original file line number Diff line number Diff line change
@@ -1,236 +1,147 @@
const os = require('os');
const {child_process: cp, fs, path} = require('extra-build');
const {git, github, package} = require('extra-build');
const {javascript, jsdoc, markdown} = require('extra-build');

const build = require('extra-build');

const owner = 'nodef';
const repo = build.readMetadata('.').name;
const srcts = 'index.ts';
const outjs = 'index.js';
const outmjs = 'index.mjs';
const outdts = 'index.d.ts';




function isSubmodule(pth) {
if (/^_|index.ts$/.test(pth)) return false;
if (!/\.ts$/.test(pth)) return false;
return true;
}
const LOCATIONS = [
'src/index.ts',
];


// Get filename keywords for main/sub package.
function filenameKeywords(fil) {
if (fil !== srcts) return [path.symbolname(fil)];
return fs.readdirSync('src').filter(isSubmodule).map(path.symbolname);
}


// Get export keywords for main/sub package.
function exportKeywords(fil) {
var txt = fs.readFileTextSync(`src/${fil}`);
var exps = javascript.jsdocSymbols(txt).filter(s => s.export);
return exps.map(e => path.symbolname(e.name));
}


// Get keywords for main/sub package.
function keywords(fil) {
var m = package.read('.');
var s = new Set([...m.keywords, ...filenameKeywords(fil), ...exportKeywords(fil)]);
function keywords(ds, less=false) {
var rkind = /namespace|function/i;
var ds = less? ds.filter(d => rkind.test(d.kind)) : ds;
var m = build.readMetadata('.');
var s = new Set([...m.keywords, ...ds.map(d => d.name)]);
return Array.from(s);
}


// Update GitHub details.
function updateGithub() {
var m = package.read('.');
var {name, description} = m;
var homepage = `https://www.npmjs.com/package/${name}`;
var topics = keywords(srcts);
topics.length = Math.min(topics.length, 20);
github.updateDetails(owner, name, {description, homepage, topics});
// Publish a root package to NPM, GitHub.
function publishRootPackage(ds, ver, typ) {
var _package = build.readDocument('package.json');
var _readme = build.readDocument('README.md');
var m = build.readMetadata('.');
var md = build.readFileText('README.md');
m.version = ver;
m.keywords = keywords(ds);
if (typ) {
m.name = `${m.name}.${typ}`;
m.description = m.description.replace(/\.$/, ` {${typ}}.`);
md = md.replace(/(unpkg\.com\/)(\S+?)(\/\))/, `$1$2.${typ}$3`);
}
build.writeMetadata('.', m);
build.writeFileText('README.md', md);
build.publish('.');
try { build.publishGithub('.', owner); }
catch {}
build.writeDocument(_package);
build.writeDocument(_readme);
}


// Generate and publish docs.
function publishDocs(fil) {
var url = git.remoteUrl();
var cwd = fs.mkdtempSync(path.join(os.tmpdir(), '.docs'));
cp.execLogSync(`git clone ${url} "${cwd}"`);
try { cp.execLogSync(`git checkout gh-pages`, {cwd}); }
catch(e) { git.setupBranch('gh-pages', {cwd}); }
cp.execLogSync(`typedoc "src/${fil}" --out ".docs"`);
cp.execLogSync(`rm -rf "${cwd}"/*`);
cp.execLogSync(`mv ".docs"/* "${cwd}"/`);
git.commitPush('', {cwd});
cp.execLogSync(`rm -rf ${cwd}`);
// Transform JSDoc in .d.ts file.
function transformJsdoc(x, dm) {
if (!dm.has(x.name)) return null;
var link = `[📘](https://github.com/${owner}/${repo}/wiki/${x.name})`;
x.description = x.description.replace(/\[📘\]\(.+?\)/g, '');
x.description = x.description.trim() + '\n' + link;
return x;
}


// Webify output files.
function webifyMain(sym) {
cp.execLogSync(`browserify "${outjs}" -o "${outjs}.1" -s ${sym}`);
cp.execLogSync(`cp "${outmjs}" "${outmjs}.1"`);
cp.execLogSync(`terser "${outjs}.1" -o "${outjs}" -c -m`);
cp.execLogSync(`terser "${outmjs}.1" -o "${outmjs}" -c -m`);
cp.execLogSync(`rm -f "${outjs}.1"`);
cp.execLogSync(`rm -f "${outmjs}.1"`);
// Bundle script for test or publish.
function bundleScript(ds) {
var dm = new Map(ds.map(d => [d.name, d]));
build.exec(`tsc`);
build.bundleScript(`.build/${srcts}`);
build.jsdocifyScript('index.d.ts', 'index.d.ts', x => transformJsdoc(x, dm));
}


// Generate main output files.
function generateMain(fil, sym) {
var bld = fil.replace(/\.ts/, '.js');
var env = sym? ` --environment TYPE:web` : '';
cp.execLogSync(`rollup -c rollup.config.js -i .build/${bld}` + env);
if (sym) webifyMain(sym);
// Publish root packages to NPM, GitHub.
function publishRootPackages(ds, ver) {
var m = build.readMetadata('.');
var sym = build.symbolname(m.name);
bundleScript(ds);
publishRootPackage(ds, ver, '');
build.webifyScript('index.mjs', 'index.mjs', {format: 'esm'});
build.webifyScript('index.js', 'index.js', {format: 'cjs', symbol: sym});
publishRootPackage(ds, ver, 'web');
}


// Publish root package to NPM, GitHub.
function publishRoot(sym, ver) {
fs.restoreFileSync('package.json', () => {
var m = package.read();
m.version = ver;
m.keywords = keywords(srcts);
if (sym) { m.name += '.web'; }
fs.restoreFileSync('README.md', () => {
var txt = fs.readFileTextSync('README.md');
if (sym) txt = txt.replace(/\[Files\]\((.*?)\/\)/g, '[Files]($1.web/)');
fs.writeFileTextSync('README.md', txt);
package.write('.', m);
package.publish('.');
package.publishGithub('.', owner);
});
});
// Publish docs.
function publishDocs(ds) {
build.updateGithubRepoDetails({owner, repo, topics: keywords(ds, true)});
build.generateDocs(`src/${srcts}`);
build.publishDocs();
}


// Deploy root package to NPM, GitHub.
function deployRoot(ver) {
var m = package.read();
var sym = path.symbolname(m.name);
generateMain(srcts, '');
publishRoot('', ver);
generateMain(srcts, sym);
publishRoot(sym, ver);
// Pushish root, sub packages to NPM, GitHub.
function publishPackages(ds) {
var m = build.readMetadata('.');
var ver = build.nextUnpublishedVersion(m.name, m.version);
publishRootPackages(ds, ver);
}


// Deploy root, sub packages to NPM, GitHub.
function deployAll() {
var m = package.read();
var ver = package.nextUnpublishedVersion(m.name, m.version);
cp.execLogSync(`tsc`);
updateGithub();
publishDocs(srcts);
deployRoot(ver);
// deploySub(ver);
// Generate wiki for all exported symbols.
function generateWiki() {
// createWikiFiles();
// generateWikiFiles();
}


// Source export symbols.
function sourceExportSymbols() {
var a = new Map();
var f = 'index.ts';
var txt = fs.readFileTextSync(`src/${f}`);
var exps = javascript.exportSymbols(txt);
for (var exp of exps)
a.set(exp.name, exp);
// Get README index descriptions.
function readmeDescription(d) {
var rkind = /namespace|function/i;
var sname = /a?sync$/i;
if (!rkind.test(d.kind)) return '';
if (sname.test(d.name) && d.name!=='spawnAsync') return '';
var a = d.description.replace(/The.+method/, 'This method');
a = a.replace(', with command-line arguments in ', ' and ');
a = a.replace(/(\S)`(.*?)`/, '$1 `$2`');
return a;
}

// Source JSDoc symbols.
function sourceJsdocSymbols() {
var a = new Map();
for (var f of fs.readdirSync('src')) {
var txt = fs.readFileTextSync(`src/${f}`);
var docs = javascript.jsdocSymbols(txt);
for (var doc of docs)
a.set(doc.name, doc);
}
return a;
}


// Update index table for README.
function updateMarkdownIndex(rkind) {
var out = 'README.md';
var exps = sourceExportSymbols();
var docs = sourceJsdocSymbols();
var txt = fs.readFileTextSync(out);
txt = markdown.replaceTables(txt, (full, rows) => {
if (rows.length < 1 || rows[0].length < 2) return full;
rows = rows.map(r => [r[0].trim(), r[1].trim()]);
if (!/property/i.test(rows[0][0])) return full;
if (!/description/i.test(rows[0][1])) return full;
var rmap = new Map(rows.map((r, i) => [r[0], i]));
for (var e of exps.values()) {
if (!docs.has(e.name)) continue;
// if (!rkind.test(e.kind)) continue;
var key = `[${e.name}]`;
var val = jsdoc.parse(docs.get(e.name).jsdoc).description.trim();
if (!rmap.has(key)) rows.push([key, val]);
else rows[rmap.get(key)][1] = val;
}
var top = '| ' + rows[0].join(' | ') + ' |\n';
var mid = '| ' + rows[0].map(r => ` ---- `).join(' | ') + ' |\n';
var bot = rows.slice(1).map(r => '| ' + r.join(' | ') + ' |\n').join('');
return top + mid + bot;
});
fs.writeFileTextSync(out, txt);
}


// Get docs link reference for jsdoc symbol.
function docsLinkReference(sym, pre, repo) {
var d = jsdoc.parse(sym.jsdoc);
var root = `https://${owner}.github.io/${repo}`;
var name = sym.name;
var pred = pre? `${pre}.` : '';
var prem = pre? `modules/${pre}.html` : 'modules.html';
switch (d.kind) {
case 'interface': return `[${name}]: ${root}/interfaces/${pred}${name}.html`;
case 'class': return `[${name}]: ${root}/classes/${pred}${name}.html`;
default: return `[${name}]: ${root}/${prem}#${name}`;
}
}


// Update link references for README.
function updateMarkdownLinkReferences() {
var m = package.read('.');
var out = 'README.md', pre = '';
var docs = sourceJsdocSymbols();
if (!fs.existsSync(out)) return;
var txt = fs.readFileTextSync(out);
txt = markdown.replaceLinkReferences(txt, (full, name) => {
if (!docs.has(name)) return full;
return docsLinkReference(docs.get(name), pre, m.name);
});
var lset = new Set(markdown.links(txt).filter(x => !x.url).map(x => x.ref || x.name));
var rset = new Set(markdown.linkReferences(txt).map(x => x.name));
for (var l of lset) {
if (rset.has(l)) continue;
if (!docs.has(l)) continue;
txt += docsLinkReference(docs.get(l), pre, m.name) + '\n';
}
fs.writeFileTextSync(out, txt);
// Sort docs details by original order.
function compareLocation(a, b) {
if (a.kind!==b.kind) return 0;
var alocn = a.location.replace(/.*?@types\/node.*?\:/, 'src/_file.ts:');
var blocn = b.location.replace(/.*?@types\/node.*?\:/, 'src/_file.ts:');
var [afile] = alocn.split(':');
var [bfile] = blocn.split(':');
return LOCATIONS.indexOf(afile) - LOCATIONS.indexOf(bfile) || alocn.localeCompare(blocn);
}


// Update markdowns README, wiki.
function updateMarkdown() {
updateMarkdownIndex(/const|class|(async\s+)?function\*?/);
updateMarkdownLinkReferences();
// Update README.
function updateReadme(ds) {
var m = build.readMetadata('.');
var repo = m.name;
var ds = ds.slice().sort(compareLocation);
var dm = new Map(ds.map(d => [d.name, d]));
var txt = build.readFileText('README.md');
txt = build.wikiUpdateIndex(txt, dm, readmeDescription);
txt = build.wikiUpdateLinkReferences(txt, dm, {owner, repo, useWiki: true});
build.writeFileText('README.md', txt);
}


// Finally.
function main(a) {
if (a[2] === 'deploy') deployAll();
else if (a[2] === 'markdown') updateMarkdown();
else generateMain(srcts, '');
var p = build.loadDocs([`src/${srcts}`]);
var ds = p.children.map(build.docsDetails);
if (a[2]==='wiki') generateWiki(ds);
else if (a[2]==='readme') updateReadme(ds);
else if (a[2]==='publish-docs') publishDocs(ds);
else if (a[2]==='publish-packages') publishPackages(ds);
else bundleScript(ds);
}
main(process.argv);
Loading

0 comments on commit 36ee913

Please sign in to comment.