wikity 1.2.1 → 1.3.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/bin/index.js +2 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +58 -0
- package/dist/common.d.ts +20 -0
- package/{src → dist}/common.js +7 -14
- package/{src → dist}/compile.d.ts +3 -3
- package/dist/compile.js +139 -0
- package/{src → dist}/index.d.ts +11 -8
- package/dist/index.js +11 -0
- package/{src → dist}/parse.d.ts +3 -3
- package/dist/parse.js +259 -0
- package/dist/wiki.css.d.ts +2 -0
- package/dist/wiki.css.js +31 -0
- package/license.md +1 -1
- package/package.json +59 -54
- package/readme.md +54 -22
- package/changelog.md +0 -44
- package/src/cli.d.ts +0 -2
- package/src/cli.js +0 -44
- package/src/common.d.ts +0 -13
- package/src/compile.js +0 -135
- package/src/index.js +0 -8
- package/src/parse.js +0 -170
package/bin/index.js
ADDED
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const VERSION = require('../package.json').version;
|
|
7
|
+
const index_1 = __importDefault(require("./index"));
|
|
8
|
+
// Helper functions
|
|
9
|
+
const indent = (n) => ' '.repeat(n * 4);
|
|
10
|
+
const usage = (command, ...desc) => {
|
|
11
|
+
console.log('\n' + indent(2) + command);
|
|
12
|
+
desc.forEach((msg) => console.log(indent(2.5) + msg));
|
|
13
|
+
};
|
|
14
|
+
const arg = (n) => process.argv[n + 1] || '';
|
|
15
|
+
const args = process.argv.slice(1);
|
|
16
|
+
// Run CLI
|
|
17
|
+
if (!arg(1)) {
|
|
18
|
+
// No arguments
|
|
19
|
+
console.log('Type `wikity help` for a list of commands.');
|
|
20
|
+
}
|
|
21
|
+
else if (arg(1).includes('h')) {
|
|
22
|
+
// Show help message
|
|
23
|
+
console.log(`\n${indent(1)}Wikity CLI commands:`);
|
|
24
|
+
usage(`wikity (help|-h)`, `Display this help message.`);
|
|
25
|
+
usage(`wikity compile [<folder>] [-o <folder>] [-t <folder>] -i <folder>] [-e] [-d]`, `Compile wikitext files from a given folder.`, ` [<folder>]\n${indent(3.5)}Input folder ('.' (current folder) if unset).`, ` (-o|--outputFolder) <folder>\n${indent(3.5)}Folder that compiled HTML files are placed in ('wikity-out' if unset).`, ` (-t|--templatesFolder) <folder>\n${indent(3.5)}Where to place wiki templates ('templates' if unset).`, ` (-i|--imagesFolder) <folder>\n${indent(3.5)}Where to place wiki images ('images' if unset).`, ` (-e|--eleventy)\n${indent(3.5)}Compiles files with Eleventy front matter (false if unset).`, ` (-d|--defaultStyles)\n${indent(3.5)}Add default wiki styling to all pages (true if unset).`);
|
|
26
|
+
usage(`wikity (parse|-p) "<input>"`, `Parse raw wikitext from the command line.`);
|
|
27
|
+
usage(`wikity (version|-v)`, `Display the current version of Wikity.`);
|
|
28
|
+
}
|
|
29
|
+
else if (arg(1).includes('c')) {
|
|
30
|
+
// Run compilation
|
|
31
|
+
const configArgs = args.slice(2);
|
|
32
|
+
const argsList = configArgs.join(' ');
|
|
33
|
+
// retrieve item from arguments list
|
|
34
|
+
const getArgContent = (arg) => arg.test(argsList) && configArgs.filter((_, i) => arg.test(configArgs[i - 1])).join(' ') || '';
|
|
35
|
+
// Fetch user-supplied arguments
|
|
36
|
+
const folder = arg(2) || '.';
|
|
37
|
+
const outputFolder = getArgContent(/^-+o/);
|
|
38
|
+
const templatesFolder = getArgContent(/^-+t/);
|
|
39
|
+
const imagesFolder = getArgContent(/^-+i/);
|
|
40
|
+
const eleventy = /^-+e/.test(argsList);
|
|
41
|
+
const defaultStyles = /^-+d/.test(argsList);
|
|
42
|
+
index_1.default.compile(folder, { outputFolder, templatesFolder, imagesFolder, eleventy, defaultStyles });
|
|
43
|
+
}
|
|
44
|
+
else if (arg(1).includes('p')) {
|
|
45
|
+
// Run parsing
|
|
46
|
+
// second argument is inputted text
|
|
47
|
+
const input = arg(2);
|
|
48
|
+
const output = index_1.default.parse(input);
|
|
49
|
+
console.log(output);
|
|
50
|
+
}
|
|
51
|
+
else if (arg(1).includes('v')) {
|
|
52
|
+
// Show version
|
|
53
|
+
console.log('The current version of Wikity is ' + VERSION);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Unknown command
|
|
57
|
+
console.log('Unknown command; type `wikity help` for help');
|
|
58
|
+
}
|
package/dist/common.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type Metadata = Record<string, any>;
|
|
2
|
+
export interface Config {
|
|
3
|
+
/** The folder that Wikity's compiled HTML files are outputted to (defaults to 'wikity-out') */
|
|
4
|
+
outputFolder?: string;
|
|
5
|
+
/** The folder that wiki templates are to be stored in relative to the root folder */
|
|
6
|
+
templatesFolder?: string;
|
|
7
|
+
/** The folder that images are to be stored in relative to the root folder */
|
|
8
|
+
imagesFolder?: string;
|
|
9
|
+
/** Whether to be set up for Eleventy integration (defaults to false) */
|
|
10
|
+
eleventy?: boolean;
|
|
11
|
+
/** Whether to use default wiki styling (defaults to true) */
|
|
12
|
+
defaultStyles?: boolean;
|
|
13
|
+
/** Custom CSS styles to add to the wiki pages */
|
|
14
|
+
customStyles?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface Result {
|
|
17
|
+
data: string;
|
|
18
|
+
metadata: Metadata;
|
|
19
|
+
}
|
|
20
|
+
export declare function RegExpBuilder(regex: string, flag?: string): RegExp;
|
package/{src → dist}/common.js
RENAMED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RegExpBuilder =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
exports.Result = Result;
|
|
11
|
-
function RegExpBuilder(regex, flag = 'mgi') {
|
|
12
|
-
return RegExp(regex.replace(/ /g, '').replace(/\|\|.+?\|\|/g, ''), flag);
|
|
13
|
-
}
|
|
14
|
-
exports.RegExpBuilder = RegExpBuilder;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RegExpBuilder = void 0;
|
|
4
|
+
function RegExpBuilder(regex, flag = 'mgi') {
|
|
5
|
+
return RegExp(regex.replace(/ /g, '').replace(/\|\|.+?\|\|/g, ''), flag);
|
|
6
|
+
}
|
|
7
|
+
exports.RegExpBuilder = RegExpBuilder;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { Config } from './common';
|
|
2
|
-
export declare function eleventyCompile(dir?: string, config?: Config): void;
|
|
3
|
-
export declare function compile(dir?: string, config?: Config): void;
|
|
1
|
+
import { Config } from './common';
|
|
2
|
+
export declare function eleventyCompile(dir?: string, config?: Config): void;
|
|
3
|
+
export declare function compile(dir?: string, config?: Config): void;
|
package/dist/compile.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.compile = exports.eleventyCompile = void 0;
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const glob = require('glob');
|
|
9
|
+
const dedent = require('dedent');
|
|
10
|
+
const formatter = require('html-formatter');
|
|
11
|
+
const parse_1 = require("./parse");
|
|
12
|
+
const common_1 = require("./common");
|
|
13
|
+
const wiki_css_1 = __importDefault(require("./wiki.css"));
|
|
14
|
+
const r = String.raw;
|
|
15
|
+
function eleventyCompile(dir = '.', config = {}) {
|
|
16
|
+
compile(dir, { eleventy: true, ...config });
|
|
17
|
+
}
|
|
18
|
+
exports.eleventyCompile = eleventyCompile;
|
|
19
|
+
function compile(dir = '.', config = {}) {
|
|
20
|
+
let stylesCreated = false;
|
|
21
|
+
// Write wikitext files
|
|
22
|
+
const files = glob.sync(dir + "/**/*.wiki", {});
|
|
23
|
+
files.forEach((file) => {
|
|
24
|
+
var _a;
|
|
25
|
+
const fileData = fs.readFileSync(file, { encoding: 'utf8' });
|
|
26
|
+
const { data: parsedContent, metadata } = (0, parse_1.parse)(fileData, config);
|
|
27
|
+
let outText = parsedContent;
|
|
28
|
+
const templatesFolder = config.templatesFolder || 'templates';
|
|
29
|
+
const imagesFolder = config.imagesFolder || 'images';
|
|
30
|
+
const outputFolder = config.outputFolder || 'wikity-out';
|
|
31
|
+
const [, folder, filename] = file.match((0, common_1.RegExpBuilder)(r `^(.+?[\/\\]) ((?:(?:${templatesFolder}|${imagesFolder})[\/\\])?[^\/\\]+)$`, '')) || [];
|
|
32
|
+
const outFolder = dir + '/' + outputFolder + '/';
|
|
33
|
+
const outFilename = filename.replace(/ /g, '_').replace('.wiki', '.html');
|
|
34
|
+
const urlPath = outFilename.replace(/(?<=^|\/)\w/g, m => m.toUpperCase());
|
|
35
|
+
const displayTitle = metadata.displayTitle || urlPath.replace('.html', '');
|
|
36
|
+
// Eleventy configuration
|
|
37
|
+
const frontMatter = config.eleventy ? dedent `
|
|
38
|
+
---
|
|
39
|
+
permalink: /wiki/${urlPath}
|
|
40
|
+
---
|
|
41
|
+
` : '';
|
|
42
|
+
// Create TOC
|
|
43
|
+
if (!metadata.notoc && (metadata.toc || (((_a = outText.match(/<h\d[^>]*>/g)) === null || _a === void 0 ? void 0 : _a.length) || 0) > 3)) {
|
|
44
|
+
let toc = '';
|
|
45
|
+
let headings = Array.from(parsedContent.match(/<h\d[^>]*>.+?<\/h\d>/gs) || []);
|
|
46
|
+
headings.forEach(match => {
|
|
47
|
+
var _a;
|
|
48
|
+
const text = match.replace(/\s*<\/?h\d[^>]*>\s*/g, '');
|
|
49
|
+
const lvl = +(((_a = match.match(/\d/g)) === null || _a === void 0 ? void 0 : _a[0]) || -1);
|
|
50
|
+
toc += `${`<ol>`.repeat(lvl - 1)} <li> <a href="#${encodeURI(text.replace(/ /g, '_'))}">${text}</a> </li> ${`</ol>`.repeat(lvl - 1)}`;
|
|
51
|
+
});
|
|
52
|
+
const tocElem = dedent `
|
|
53
|
+
<div id="toc">
|
|
54
|
+
<span id="toc-heading">
|
|
55
|
+
<strong>Contents</strong>
|
|
56
|
+
[<a href="javascript:void(0)" onclick="
|
|
57
|
+
document.querySelector('#toc ol').setAttribute('style', this.innerText === 'hide' ? 'display: none;' : '');
|
|
58
|
+
this.innerText = this.innerText === 'hide' ? 'show' : 'hide';
|
|
59
|
+
">hide</a>]
|
|
60
|
+
</span>
|
|
61
|
+
<ol>${toc}</ol>
|
|
62
|
+
</div>
|
|
63
|
+
`;
|
|
64
|
+
// Set TOC on page
|
|
65
|
+
if (outText.includes('<toc></toc>'))
|
|
66
|
+
outText = outText.replace('<toc></toc>', tocElem);
|
|
67
|
+
else
|
|
68
|
+
outText = outText.replace(/<h\d[^>]*>/, tocElem + '$&');
|
|
69
|
+
}
|
|
70
|
+
// Create plaintext of HTML for use as description/metadata property.
|
|
71
|
+
const plaintextData = parsedContent.replace(/<.+?>/gs, ' ');
|
|
72
|
+
// Create HTML
|
|
73
|
+
const html = dedent `
|
|
74
|
+
<html>
|
|
75
|
+
<head>
|
|
76
|
+
<meta charset="utf-8">
|
|
77
|
+
<meta name="viewport" content="initial-scale=1.0, width=device-width">
|
|
78
|
+
<meta name="description" content="${plaintextData.substring(0, 256)}...">
|
|
79
|
+
<title>${displayTitle}</title>
|
|
80
|
+
<link id="default-styles" rel="stylesheet" href="/wiki.css">
|
|
81
|
+
</head>
|
|
82
|
+
<body>
|
|
83
|
+
<header>
|
|
84
|
+
<h1 id="page-title">${displayTitle}</h1>
|
|
85
|
+
</header>
|
|
86
|
+
<main>
|
|
87
|
+
<p>\n${outText}
|
|
88
|
+
</p>
|
|
89
|
+
</main>
|
|
90
|
+
<footer>
|
|
91
|
+
<p id="credit_wikity">Created using <a href="https://github.com/Nixinova/Wikity">Wikity</a></p>
|
|
92
|
+
</footer>
|
|
93
|
+
</body>
|
|
94
|
+
</html>
|
|
95
|
+
`;
|
|
96
|
+
// Write to file
|
|
97
|
+
for (const path of ['', templatesFolder, imagesFolder]) {
|
|
98
|
+
if (!fs.existsSync(outFolder + path)) {
|
|
99
|
+
fs.mkdirSync(outFolder + path);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
;
|
|
103
|
+
const renderedHtml = formatter.render(html).replace(/(<\/\w+>)(\S)/g, '$1 $2');
|
|
104
|
+
fs.writeFileSync(outFolder + outFilename, frontMatter + '\n' + renderedHtml, 'utf8');
|
|
105
|
+
// Move images
|
|
106
|
+
glob(imagesFolder + '/*', {}, (err, files) => {
|
|
107
|
+
if (err) {
|
|
108
|
+
console.warn(err);
|
|
109
|
+
}
|
|
110
|
+
const outImagesFolder = outFolder + imagesFolder + '/';
|
|
111
|
+
if (!fs.existsSync(outImagesFolder)) {
|
|
112
|
+
fs.mkdirSync(outImagesFolder);
|
|
113
|
+
}
|
|
114
|
+
for (const file of files) {
|
|
115
|
+
fs.copyFileSync(file, outImagesFolder + file.split(/[/\\]/).pop());
|
|
116
|
+
}
|
|
117
|
+
;
|
|
118
|
+
});
|
|
119
|
+
// Create site styles
|
|
120
|
+
if (!stylesCreated) {
|
|
121
|
+
stylesCreated = true;
|
|
122
|
+
let styles = '';
|
|
123
|
+
if (config.defaultStyles !== false) {
|
|
124
|
+
styles += wiki_css_1.default;
|
|
125
|
+
}
|
|
126
|
+
if (config.customStyles) {
|
|
127
|
+
styles += config.customStyles;
|
|
128
|
+
}
|
|
129
|
+
const cssOutput = dedent `
|
|
130
|
+
---
|
|
131
|
+
permalink: /wiki.css
|
|
132
|
+
---
|
|
133
|
+
${styles}
|
|
134
|
+
`;
|
|
135
|
+
fs.writeFileSync(outFolder + 'wiki.css.njk', cssOutput);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
exports.compile = compile;
|
package/{src → dist}/index.d.ts
RENAMED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { compile, eleventyCompile } from './compile';
|
|
2
|
-
import { rawParse } from './parse';
|
|
3
|
-
declare const _default: {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { compile, eleventyCompile } from './compile';
|
|
2
|
+
import { rawParse } from './parse';
|
|
3
|
+
declare const _default: {
|
|
4
|
+
/** Parse wikitext from a string */
|
|
5
|
+
parse: typeof rawParse;
|
|
6
|
+
/** Compile a folder of wikitext files */
|
|
7
|
+
compile: typeof compile;
|
|
8
|
+
/** Compile a folder of wikitext files for output in Eleventy @deprecated */
|
|
9
|
+
eleventyPlugin: typeof eleventyCompile;
|
|
10
|
+
};
|
|
11
|
+
export = _default;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const compile_1 = require("./compile");
|
|
3
|
+
const parse_1 = require("./parse");
|
|
4
|
+
module.exports = {
|
|
5
|
+
/** Parse wikitext from a string */
|
|
6
|
+
parse: parse_1.rawParse,
|
|
7
|
+
/** Compile a folder of wikitext files */
|
|
8
|
+
compile: compile_1.compile,
|
|
9
|
+
/** Compile a folder of wikitext files for output in Eleventy @deprecated */
|
|
10
|
+
eleventyPlugin: compile_1.eleventyCompile,
|
|
11
|
+
};
|
package/{src → dist}/parse.d.ts
RENAMED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { Config, Result } from './common';
|
|
2
|
-
export declare function rawParse(data: string, config?: Config): string;
|
|
3
|
-
export declare function parse(data: string, config?: Config): Result;
|
|
1
|
+
import { Config, Result } from './common';
|
|
2
|
+
export declare function rawParse(data: string, config?: Config): string;
|
|
3
|
+
export declare function parse(data: string, config?: Config): Result;
|
package/dist/parse.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parse = exports.rawParse = void 0;
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const htmlEscape = require('escape-html');
|
|
6
|
+
const dateFormat = require('dateformat');
|
|
7
|
+
const common_1 = require("./common");
|
|
8
|
+
const r = String.raw;
|
|
9
|
+
const MAX_RECURSION = 20;
|
|
10
|
+
const MAX_ARG_COUNT = 10;
|
|
11
|
+
const arg = r `\s*([^|}]+?)\s*`;
|
|
12
|
+
function rawParse(data, config = {}) {
|
|
13
|
+
return parse(data, config).data;
|
|
14
|
+
}
|
|
15
|
+
exports.rawParse = rawParse;
|
|
16
|
+
function parse(data, config = {}) {
|
|
17
|
+
const templatesFolder = config.templatesFolder || 'templates';
|
|
18
|
+
const imagesFolder = config.imagesFolder || 'images';
|
|
19
|
+
const vars = {};
|
|
20
|
+
const metadata = {};
|
|
21
|
+
const nowikis = [];
|
|
22
|
+
const refs = [];
|
|
23
|
+
let nowikiCount = 0;
|
|
24
|
+
let rawExtLinkCount = 0;
|
|
25
|
+
let refCount = 0;
|
|
26
|
+
let outText = data;
|
|
27
|
+
let stylesCreated = false;
|
|
28
|
+
for (let l = 0, last = ''; l < MAX_RECURSION; l++) {
|
|
29
|
+
if (last === outText)
|
|
30
|
+
break;
|
|
31
|
+
last = outText;
|
|
32
|
+
outText = outText
|
|
33
|
+
// Nowiki: <nowiki></nowiki>
|
|
34
|
+
.replace((0, common_1.RegExpBuilder)(r `<nowiki> ([^]+?) </nowiki>`), (_, m) => `%NOWIKI#${nowikis.push(m), nowikiCount++}%`)
|
|
35
|
+
// Sanitise unacceptable HTML
|
|
36
|
+
.replace((0, common_1.RegExpBuilder)(r `<(/?) \s* (?= script|link|meta|iframe|frameset|object|embed|applet|form|input|button|textarea )`), '<$1')
|
|
37
|
+
.replace((0, common_1.RegExpBuilder)(r `(?<= <[^>]+ ) (\bon(\w+))`), 'data-$2')
|
|
38
|
+
// Comments: <!-- -->
|
|
39
|
+
.replace(/<!--[^]+?-->/g, '')
|
|
40
|
+
// Lines: ----
|
|
41
|
+
.replace(/^-{4,}/gm, '<hr>')
|
|
42
|
+
// Metadata: displayTitle, __NOTOC__, etc
|
|
43
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* displayTitle: ([^}]+) }}`), (_, title) => (metadata.displayTitle = title, ''))
|
|
44
|
+
.replace((0, common_1.RegExpBuilder)(r `__NOINDEX__`), () => (metadata.noindex = true, ''))
|
|
45
|
+
.replace((0, common_1.RegExpBuilder)(r `__NOTOC__`), () => (metadata.notoc = true, ''))
|
|
46
|
+
.replace((0, common_1.RegExpBuilder)(r `__FORCETOC__`), () => (metadata.toc = true, ''))
|
|
47
|
+
.replace((0, common_1.RegExpBuilder)(r `__TOC__`), () => (metadata.toc = true, `<toc></toc>`))
|
|
48
|
+
// Magic words: {{!}}, {{reflist}}, etc
|
|
49
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* ! \s* }}`), '|')
|
|
50
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* = \s* }}`), '=')
|
|
51
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* [Rr]eflist \s* }}`), '<references/>')
|
|
52
|
+
// String functions: {{lc:}}, {{ucfirst:}}, {{len:}}, etc
|
|
53
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? urlencode: ${arg} }}`), (_, m) => encodeURI(m))
|
|
54
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? urldecode: ${arg} }}`), (_, m) => decodeURI(m))
|
|
55
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? lc: ${arg} }}`), (_, m) => m.toLowerCase())
|
|
56
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? uc: ${arg} }}`), (_, m) => m.toUpperCase())
|
|
57
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? lcfirst: ${arg} }}`), (_, m) => m[0].toLowerCase() + m.substr(1))
|
|
58
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? ucfirst: ${arg} }}`), (_, m) => m[0].toUpperCase() + m.substr(1))
|
|
59
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? len: ${arg} }}`), (_, m) => m.length)
|
|
60
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? pos: ${arg} \|${arg} (?: \s*\|${arg} )? }}`), (_, find, str, n = 0) => find.substr(n).indexOf(str))
|
|
61
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? sub: ${arg} \|${arg} (?:\|${arg})? }}`), (_, str, from, len) => str.substr(+from - 1, +len))
|
|
62
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? padleft: ${arg} \|${arg} \|${arg} }}`), (_, str, n, char) => str.padStart(+n, char))
|
|
63
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? padright: ${arg} \|${arg} \|${arg} }}`), (_, str, n, char) => str.padEnd(+n, char))
|
|
64
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? replace: ${arg} \|${arg} \|${arg} }}`), (_, str, find, rep) => str.split(find).join(rep))
|
|
65
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? explode: ${arg} \|${arg} \|${arg} }}`), (_, str, delim, pos) => str.split(delim)[+pos])
|
|
66
|
+
// Parser functions: {{#if:}}, {{#switch:}}, etc
|
|
67
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* (#\w+) \s* : \s* ( [^{}]+ ) \s* }} ( ?!} )`), (_, name, content) => {
|
|
68
|
+
if (/{{\s*#/.test(content))
|
|
69
|
+
return _;
|
|
70
|
+
const args = content.trim().split(/\s*\|\s*/);
|
|
71
|
+
switch (name) {
|
|
72
|
+
case '#if':
|
|
73
|
+
return (args[0] ? args[1] : args[2]) || '';
|
|
74
|
+
case '#ifeq':
|
|
75
|
+
return (args[0] === args[1] ? args[2] : args[3]) || '';
|
|
76
|
+
case '#vardefine':
|
|
77
|
+
vars[args[0]] = args[1] || '';
|
|
78
|
+
return '';
|
|
79
|
+
case '#var':
|
|
80
|
+
if ((0, common_1.RegExpBuilder)(r `{{ \s* #vardefine \s* : \s* ${args[0]}`).test(outText))
|
|
81
|
+
return _; // wait until var is set
|
|
82
|
+
return vars[args[0]] || args[1] || '';
|
|
83
|
+
case '#switch':
|
|
84
|
+
return args.slice(1)
|
|
85
|
+
.map(arg => arg.split(/\s*=\s*/))
|
|
86
|
+
.filter(duo => args[0] === duo[0].replace('#default', args[0]))[0][1];
|
|
87
|
+
case '#time':
|
|
88
|
+
case '#date':
|
|
89
|
+
case '#datetime':
|
|
90
|
+
// make sure the characters are not inside a string
|
|
91
|
+
let parsedMatch = args[0].replace(/".+?"/g, '').replace(/'.+?'/g, '');
|
|
92
|
+
if (/[abcefgijkqruvx]/i.test(parsedMatch)) {
|
|
93
|
+
const errMsg = `Wikity does not use Wikipedia's #time function syntax. Use repetition-based formatting (e.g. yyyy-mm-dd) instead.`;
|
|
94
|
+
console.warn(`<Wikity> [WARN] ${errMsg}`);
|
|
95
|
+
}
|
|
96
|
+
return dateFormat(args[1] ? new Date(args[1]) : new Date(), args[0]);
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
// Templates: {{template}}
|
|
100
|
+
.replace((0, common_1.RegExpBuilder)(r `(?<!{) {{ \s* ([^#{}|]+?) (\|[^{}]+)? }} (?!})`), (_, title, params = '') => {
|
|
101
|
+
const page = templatesFolder + '/' + title.trim().replace(/ /g, '_');
|
|
102
|
+
let content = '';
|
|
103
|
+
// Try retrieve template content
|
|
104
|
+
try {
|
|
105
|
+
content = fs.readFileSync('./' + page + '.wiki', { encoding: 'utf8' });
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Return redlink if template doesn't exist
|
|
109
|
+
return `<a class="internal-link redlink" title="${title}" href="${page}">${title}</a>`;
|
|
110
|
+
}
|
|
111
|
+
// Remove non-template sections
|
|
112
|
+
content = content
|
|
113
|
+
.replace(/<noinclude>.*?<\/noinclude>/gs, '')
|
|
114
|
+
.replace(/.*<(includeonly|onlyinclude)>|<\/(includeonly|onlyinclude)>.*/gs, '');
|
|
115
|
+
// Substitute arguments
|
|
116
|
+
const argMatch = (arg) => (0, common_1.RegExpBuilder)(r `{{{ \s* ${arg} (?:\|([^}]*))? \s* }}}`);
|
|
117
|
+
const args = params.split('|');
|
|
118
|
+
// provided key=value template arguments
|
|
119
|
+
for (let i = 1; i < args.length; i++) {
|
|
120
|
+
const parts = args[i].split('=');
|
|
121
|
+
const [arg, val] = parts[1]
|
|
122
|
+
? [parts[0], ...parts.slice(1)]
|
|
123
|
+
: [i.toString(), parts[0]];
|
|
124
|
+
content = content.replace(argMatch(arg), (_, defaultVal) => val || defaultVal || '');
|
|
125
|
+
}
|
|
126
|
+
return content;
|
|
127
|
+
})
|
|
128
|
+
// Unparsed arguments
|
|
129
|
+
.replace((0, common_1.RegExpBuilder)(r `{{{ \s* [^{}|]+? (?:\|([^}]*))? \s* }}}`), (_, _name, defaultVal) => {
|
|
130
|
+
return defaultVal !== null && defaultVal !== void 0 ? defaultVal : '';
|
|
131
|
+
})
|
|
132
|
+
// Images: [[File:Image.png|options|caption]]
|
|
133
|
+
.replace((0, common_1.RegExpBuilder)(r `\[\[ (?:File|Image): (.+?) (\|.+?)? \]\]`), (_, file, params = '') => {
|
|
134
|
+
if (/{{/.test(params))
|
|
135
|
+
return _;
|
|
136
|
+
const path = imagesFolder + '/' + file.trim().replace(/ /g, '_');
|
|
137
|
+
let caption = '';
|
|
138
|
+
let imageData = {};
|
|
139
|
+
let imageArgs = params.split('|').map((arg) => arg.replace(/"/g, '"'));
|
|
140
|
+
for (const param of imageArgs) {
|
|
141
|
+
if (['left', 'right', 'center', 'none'].includes(param)) {
|
|
142
|
+
imageData.float = param;
|
|
143
|
+
}
|
|
144
|
+
if (['baseline', 'sub', 'super', 'top', 'text-bottom', 'middle', 'bottom', 'text-bottom'].includes(param)) {
|
|
145
|
+
imageData.align = param;
|
|
146
|
+
}
|
|
147
|
+
else if (['border', 'frameless', 'frame', 'framed', 'thumb', 'thumbnail'].includes(param)) {
|
|
148
|
+
imageData.type = { framed: 'frame', thumbnail: 'thumb' }[param] || param;
|
|
149
|
+
if (imageData.type === 'thumb') {
|
|
150
|
+
imageData.hasCaption = true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else if (param.endsWith('px')) {
|
|
154
|
+
param.replace(/(?:(\w+)?(x))?(\w+)px/, (_, size1, auto, size2) => {
|
|
155
|
+
if (size1) {
|
|
156
|
+
Object.assign(imageData, { width: size1, height: size2 });
|
|
157
|
+
}
|
|
158
|
+
else if (auto) {
|
|
159
|
+
Object.assign(imageData, { width: 'auto', height: size2 });
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
Object.assign(imageData, { width: size2, height: 'auto' });
|
|
163
|
+
}
|
|
164
|
+
return '';
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else if (param.startsWith('upright=')) {
|
|
168
|
+
imageData.width = +param.replace('upright=', '') * 300;
|
|
169
|
+
}
|
|
170
|
+
else if (param.startsWith('link=')) {
|
|
171
|
+
imageData.link = param.replace('link=', '');
|
|
172
|
+
}
|
|
173
|
+
else if (param.startsWith('alt=')) {
|
|
174
|
+
imageData.alt = param.replace('alt=', '');
|
|
175
|
+
}
|
|
176
|
+
else if (param.startsWith('style=')) {
|
|
177
|
+
imageData.style = param.replace('style=', '');
|
|
178
|
+
}
|
|
179
|
+
else if (param.startsWith('class=')) {
|
|
180
|
+
imageData.class = param.replace('class=', '');
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
caption = param;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
let content = `
|
|
187
|
+
<figure
|
|
188
|
+
class="
|
|
189
|
+
${imageData.class || ''}
|
|
190
|
+
image-container
|
|
191
|
+
image-${imageData.type || 'default'}
|
|
192
|
+
"
|
|
193
|
+
style="
|
|
194
|
+
float: ${imageData.float || 'none'};
|
|
195
|
+
vertical-align: ${imageData.align || 'unset'};
|
|
196
|
+
${imageData.style || ''}
|
|
197
|
+
"
|
|
198
|
+
>
|
|
199
|
+
<img
|
|
200
|
+
src="${path}"
|
|
201
|
+
alt="${imageData.alt || file}"
|
|
202
|
+
width="${imageData.width || 300}"
|
|
203
|
+
height="${imageData.height || 300}"
|
|
204
|
+
>
|
|
205
|
+
${imageData.hasCaption ? `<figcaption>${caption}</figcaption>` : ''}
|
|
206
|
+
</figure>
|
|
207
|
+
`;
|
|
208
|
+
if (imageData.link) {
|
|
209
|
+
content = `<a href="/${imageData.link}" title="${imageData.link}">${content}</a>`;
|
|
210
|
+
}
|
|
211
|
+
return content;
|
|
212
|
+
})
|
|
213
|
+
// Markup: '''bold''' and '''italic'''
|
|
214
|
+
.replace((0, common_1.RegExpBuilder)(r `''' ([^']+?) '''`), '<b>$1</b>')
|
|
215
|
+
.replace((0, common_1.RegExpBuilder)(r `'' ([^']+?) ''`), '<i>$1</i>')
|
|
216
|
+
// Headings: ==heading==
|
|
217
|
+
.replace((0, common_1.RegExpBuilder)(r `^ (=+) \s* (.+?) \s* \1 \s* $`), (_, lvl, txt) => `<h${lvl.length} id="${encodeURI(txt.replace(/ /g, '_'))}">${txt}</h${lvl.length}>`)
|
|
218
|
+
// Internal links: [[Page]] and [[Page|Text]]
|
|
219
|
+
.replace((0, common_1.RegExpBuilder)(r `\[\[ ([^\]|]+?) \]\]`), `<a class="internal-link" title="$1" href="$1">$1</a>`)
|
|
220
|
+
.replace((0, common_1.RegExpBuilder)(r `\[\[ ([^\]|]+?) \| ([^\]]+?) \]\]`), `<a class="internal-link" title="$1" href="/$1">$2</a>`)
|
|
221
|
+
.replace((0, common_1.RegExpBuilder)(r `(</a>)([a-z]+)`), '$2$1')
|
|
222
|
+
// External links: [href Page] and just [href]
|
|
223
|
+
.replace((0, common_1.RegExpBuilder)(r `\[ ((?:\w+:)?\/\/ [^\s\]]+) (\s [^\]]+?)? \]`), (_, href, txt) => `<a class="external-link" href="${href}">${txt || '[' + (++rawExtLinkCount) + ']'}</a>`)
|
|
224
|
+
// Bulleted list: *item
|
|
225
|
+
.replace((0, common_1.RegExpBuilder)(r `^ (\*+) (.+?) $`), (_, lvl, txt) => `${'<ul>'.repeat(lvl.length)}<li>${txt}</li>${'</ul>'.repeat(lvl.length)}`)
|
|
226
|
+
.replace((0, common_1.RegExpBuilder)(r `</ul> (\s*?) <ul>`), '$1')
|
|
227
|
+
// Numbered list: #item
|
|
228
|
+
.replace((0, common_1.RegExpBuilder)(r `^ (#+) (.+?) $`), (_, lvl, txt) => `${'<ol>'.repeat(lvl.length)}<li>${txt}</li>${'</ol>'.repeat(lvl.length)}`)
|
|
229
|
+
.replace((0, common_1.RegExpBuilder)(r `</ol> (\s*?) <ol>`), '$1')
|
|
230
|
+
// Definition list: ;head, :item
|
|
231
|
+
.replace((0, common_1.RegExpBuilder)(r `^ ; (.+) $`), '<dl><dt>$1</dt></dl>')
|
|
232
|
+
.replace((0, common_1.RegExpBuilder)(r `^ (:+) (.+?) $`), (_, lvl, txt) => `${'<dl>'.repeat(lvl.length)}<dd>${txt}</dd>${'</dl>'.repeat(lvl.length)}`)
|
|
233
|
+
.replace((0, common_1.RegExpBuilder)(r `</dl> (\s*?) <dl>`), '$1')
|
|
234
|
+
// Tables: {|, |+, !, |-, |, |}
|
|
235
|
+
.replace((0, common_1.RegExpBuilder)(r `^ \{\| (.*?) $`), (_, attrs) => `<table ${attrs}><tr>`)
|
|
236
|
+
.replace((0, common_1.RegExpBuilder)(r `^ ! ([^]+?) (?= \n^[!|] )`), (_, content) => `<th>${content}</th>`)
|
|
237
|
+
.replace((0, common_1.RegExpBuilder)(r `^ \|\+ (.*?) $`), (_, content) => `<caption>${content}</caption>`)
|
|
238
|
+
.replace((0, common_1.RegExpBuilder)(r `^ \|[^-+}] ([^]*?) (?= \n^[!|] )`), (_, content) => `<td>${content}</td>`)
|
|
239
|
+
.replace((0, common_1.RegExpBuilder)(r `^ \|- (.*?) $`), (_, attrs) => `</tr><tr ${attrs}>`)
|
|
240
|
+
.replace((0, common_1.RegExpBuilder)(r `^ \|\}`), `</tr></table>`)
|
|
241
|
+
// References: <ref></ref>, <references/>
|
|
242
|
+
.replace((0, common_1.RegExpBuilder)(r `<ref> (.+?) </ref>`), (_, text) => {
|
|
243
|
+
refs.push(text);
|
|
244
|
+
refCount++;
|
|
245
|
+
return `<sup><a id="cite-${refCount}" class="ref" href="#ref-${refCount}">[${refCount}]</a></sup>`;
|
|
246
|
+
})
|
|
247
|
+
.replace((0, common_1.RegExpBuilder)(r `<references \s* /?>`), '<ol>' + refs.map((ref, i) => `<li id="ref-${+i + 1}"> <a href="#cite-${+i + 1}">↑</a> ${ref} </li>`).join('\n') + '</ol>')
|
|
248
|
+
// Nonstandard: ``code`` and ```code blocks```
|
|
249
|
+
.replace((0, common_1.RegExpBuilder)(r ` \`\`\` ([^\`]+?) \`\`\` `), '<pre>$1</pre>')
|
|
250
|
+
.replace((0, common_1.RegExpBuilder)(r ` \`\` ([^\`]+?) \`\` `), '<code>$1</code>')
|
|
251
|
+
// Spacing
|
|
252
|
+
.replace(/(\r?\n){2}/g, '\n</p><p>\n')
|
|
253
|
+
// Restore nowiki contents
|
|
254
|
+
.replace(/%NOWIKI#(\d+)%/g, (_, n) => htmlEscape(nowikis[n]));
|
|
255
|
+
}
|
|
256
|
+
const result = { data: outText, metadata: metadata };
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
exports.parse = parse;
|
package/dist/wiki.css.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = String.raw `
|
|
4
|
+
body {font-family: sans-serif; margin: 4em; max-width: 1000px; background: #eee;}
|
|
5
|
+
main {margin: 3em -1em; background: #fff; padding: 1em;}
|
|
6
|
+
h1, h2 {margin-bottom: 0.6em; font-weight: normal; border-bottom: 1px solid #a2a9b1;}
|
|
7
|
+
ul, ol {margin: 0.3em 0 0 1.6em; padding: 0;}
|
|
8
|
+
dt {font-weight: bold;}
|
|
9
|
+
dd, dl dl {margin-block: 0; margin-inline-start: 30px;}
|
|
10
|
+
|
|
11
|
+
figure {margin: 1em;}
|
|
12
|
+
.image-thumb, .image-frame {padding: 6px; border: 1px solid gray;}
|
|
13
|
+
figcaption {padding-top: 6px;}
|
|
14
|
+
|
|
15
|
+
table.wikitable {border-collapse: collapse;}
|
|
16
|
+
table.wikitable, table.wikitable th, table.wikitable td {border: 1px solid gray; padding: 6px;}
|
|
17
|
+
table.wikitable th {background-color: #eaecf0; text-align: center;}
|
|
18
|
+
|
|
19
|
+
a:not(:hover) {text-decoration: none;}
|
|
20
|
+
a.internal-link {color: #04a;}
|
|
21
|
+
a.internal-link:visited {color: #26d;}
|
|
22
|
+
a.external-link {color: #36b;}
|
|
23
|
+
a.external-link:visited {color: #58d;}
|
|
24
|
+
a.external-link::after {content: '\1f855';}
|
|
25
|
+
a.redlink {color: #d33;}
|
|
26
|
+
a.redlink:visited {color: #b44;}
|
|
27
|
+
|
|
28
|
+
#toc {display: inline-block; border: 1px solid #aab; padding: 8px; background-color: #f8f8f8; font-size: 95%;}
|
|
29
|
+
#toc-heading {display: block; text-align: center;}
|
|
30
|
+
#toc ol {margin: 0 0 0 1.3em;}
|
|
31
|
+
`;
|
package/license.md
CHANGED
package/package.json
CHANGED
|
@@ -1,54 +1,59 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "wikity",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Compile wikitext to HTML: wikitext as a templating language.",
|
|
5
|
-
"main": "
|
|
6
|
-
"scripts": {
|
|
7
|
-
"test": "tsc && cd test && eleventy"
|
|
8
|
-
},
|
|
9
|
-
"bin": {
|
|
10
|
-
"wikity": "
|
|
11
|
-
},
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
},
|
|
50
|
-
"
|
|
51
|
-
"@
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "wikity",
|
|
3
|
+
"version": "1.3.1",
|
|
4
|
+
"description": "Compile wikitext to HTML: wikitext as a templating language.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "tsc && cd test && eleventy"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"wikity": "bin/index.js"
|
|
11
|
+
},
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=12"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"bin/",
|
|
17
|
+
"dist/"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"compiler",
|
|
21
|
+
"compilation",
|
|
22
|
+
"wikitext",
|
|
23
|
+
"mediawiki",
|
|
24
|
+
"html",
|
|
25
|
+
"template-language",
|
|
26
|
+
"templating-language",
|
|
27
|
+
"eleventy",
|
|
28
|
+
"eleventy-plugin"
|
|
29
|
+
],
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/Nixinova/Wikity.git"
|
|
33
|
+
},
|
|
34
|
+
"author": "Nixinova (https://nixinova.com)",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/Nixinova/Wikity/issues"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/Nixinova/Wikity#readme",
|
|
39
|
+
"license": "ISC",
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"dateformat": "^4.6.3",
|
|
42
|
+
"dedent": "^0.7.0",
|
|
43
|
+
"escape-html": "^1.0.3",
|
|
44
|
+
"glob": "^7.2.3",
|
|
45
|
+
"html-formatter": "^0.1.9"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"@11ty/eleventy": ">=0.10.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"@11ty/eleventy": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "ts5.0",
|
|
57
|
+
"typescript": "~5.0.4 <5.1"
|
|
58
|
+
}
|
|
59
|
+
}
|
package/readme.md
CHANGED
|
@@ -4,11 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
# Wikity
|
|
6
6
|
|
|
7
|
-
**Wikity** is a tool that allows you to use Wikitext (used by Wikipedia, Fandom, etc) as a templating language to create HTML pages
|
|
7
|
+
**Wikity** is a tool that allows you to use Wikitext (used by Wikipedia, Fandom, etc) as a templating language to create HTML pages.
|
|
8
|
+
|
|
9
|
+
This package comes with built-in support for compilation using [Eleventy](https://11ty.dev).
|
|
8
10
|
|
|
9
11
|
## Install
|
|
10
12
|
|
|
11
13
|
Wikity is available [on npm](https://www.npmjs.com/package/wikity).
|
|
14
|
+
Install locally to use in a Node package or install globally for use from the command-line.
|
|
12
15
|
|
|
13
16
|
| Local install | Global install |
|
|
14
17
|
| -------------------- | ----------------------- |
|
|
@@ -19,25 +22,35 @@ Wikity is available [on npm](https://www.npmjs.com/package/wikity).
|
|
|
19
22
|
### Node
|
|
20
23
|
|
|
21
24
|
- `wikity.compile(folder?: string, options?: object): void`
|
|
22
|
-
- Compile all Wikitext (`.wiki`) files into HTML.
|
|
23
|
-
|
|
24
|
-
- The folder to compile (default: `.`, the current directory).
|
|
25
|
-
- `options?: object`
|
|
26
|
-
- `outputFolder?: string`
|
|
27
|
-
- Where outputted HTML files shall be placed (default: `wikity-out`).
|
|
28
|
-
- `templatesFolder?: string`
|
|
29
|
-
- What folder to place templates in (default: `'templates'`).
|
|
30
|
-
- `eleventy?: boolean`
|
|
31
|
-
- Whether [front matter](https://www.11ty.dev/docs/data-frontmatter/) will be added to the outputted HTML for Eleventy to read (default: `false`).
|
|
32
|
-
- `defaultStyles?: boolean`
|
|
33
|
-
- Whether to use default wiki styling (default: `true`).
|
|
34
|
-
- `customStyles?: string`
|
|
35
|
-
- Custom CSS to style the wiki pages (default: `''`).
|
|
36
|
-
- `wikity.eleventyPlugin(folder?: string, options?: object): void`
|
|
37
|
-
- An implementation of `compile` for use with Eleventy's `addPlugin` API.
|
|
38
|
-
- `wikity.parse(input: string, {templatesFolder?: string}?): string`
|
|
25
|
+
- Compile all Wikitext (`.wiki`) files from an input folder (defaults to the current directory, `.`) into HTML.
|
|
26
|
+
- `wikity.parse(input: string, options?: object): string`
|
|
39
27
|
- Parse raw wikitext input into HTML.
|
|
40
28
|
|
|
29
|
+
- **Options**:
|
|
30
|
+
- `eleventy: boolean = false`
|
|
31
|
+
- Whether [front matter](https://www.11ty.dev/docs/data-frontmatter/) will be added to the outputted HTML for Eleventy to read (default: `false`).
|
|
32
|
+
(**This parameter *must* be set to `true` if you want to use this with Eleventy.**)
|
|
33
|
+
- `outputFolder: string`
|
|
34
|
+
- *Used only with `compile()`.*
|
|
35
|
+
- Where outputted HTML files shall be placed.
|
|
36
|
+
- Default: `'wikity-out'`.
|
|
37
|
+
- `templatesFolder: string`
|
|
38
|
+
- What folder to place templates in.
|
|
39
|
+
- Default: `'templates'`.
|
|
40
|
+
- `imagesFolder: string`
|
|
41
|
+
- What folder to place images in.
|
|
42
|
+
- Default: `'images'`.
|
|
43
|
+
- `defaultStyles: boolean`
|
|
44
|
+
- *Used only with `compile()`.*
|
|
45
|
+
- Whether to use default wiki styling.
|
|
46
|
+
- Default: to `true` when called from `compile()` and `false` when called from `parse()`.
|
|
47
|
+
- `customStyles: string`
|
|
48
|
+
- *Used only with `compile()`.*
|
|
49
|
+
- Custom CSS styles to add to the wiki pages.
|
|
50
|
+
- Default: empty (`''`).
|
|
51
|
+
|
|
52
|
+
#### Example
|
|
53
|
+
|
|
41
54
|
```js
|
|
42
55
|
const wikity = require('wikity');
|
|
43
56
|
|
|
@@ -48,19 +61,35 @@ wikity.compile();
|
|
|
48
61
|
let html = wikity.parse(`'''bold''' [[link|text]]`); // <b>bold</b> <a href="link"...>text</a>
|
|
49
62
|
```
|
|
50
63
|
|
|
51
|
-
|
|
64
|
+
#### As an Eleventy plugin
|
|
65
|
+
|
|
66
|
+
Use Wikity along with Eleventy to have all your wiki files compiled during the build process:
|
|
52
67
|
|
|
53
68
|
```js
|
|
54
69
|
// .eleventy.js (eleventy's configuration file)
|
|
55
70
|
const wikity = require('wikity');
|
|
56
71
|
module.exports = function (eleventyConfig) {
|
|
57
|
-
const wikiFolder = '
|
|
58
|
-
const
|
|
59
|
-
const
|
|
72
|
+
const wikiFolder = 'src';
|
|
73
|
+
const templatesFolder = 'templates', imagesFolder = 'images', outputFolder = 'wikity-out'; // defaults
|
|
74
|
+
const wikityOptions = { templatesFolder, imagesFolder, outputFolder };
|
|
75
|
+
const wikityPlugin = () => wikity.compile(wikiFolder, { eleventy: true, ...wikityOptions });
|
|
60
76
|
eleventyConfig.addPlugin(wikityPlugin);
|
|
77
|
+
eleventyConfig.addPassthroughCopy({[imagesFolder]: 'wiki/' + imagesFolder}); // Eleventy does not pass through images by default
|
|
61
78
|
}
|
|
62
79
|
```
|
|
63
80
|
|
|
81
|
+
The above will use the following file structure (with some example wiki files given):
|
|
82
|
+
|
|
83
|
+
- `src/`
|
|
84
|
+
- `templates/`: Directory for wiki templates (called like `{{this}}`)
|
|
85
|
+
- `images/`: Directory to place images (called like `[[File:this]]`)
|
|
86
|
+
- `wikity-out/`: File templates compiled from the `.wiki` files (add this to `.gitignore`)
|
|
87
|
+
- `Index.wiki`: Example file
|
|
88
|
+
- `Other_Page.wiki`: Example other file
|
|
89
|
+
- `wiki/`: Output HTML files compiled from wikity-out (add this to `.gitignore`)
|
|
90
|
+
|
|
91
|
+
(View the above starting at the URL path `/wiki/` when ran in an HTTP server.)
|
|
92
|
+
|
|
64
93
|
### Command-line
|
|
65
94
|
```cmd
|
|
66
95
|
$ wikity help
|
|
@@ -78,6 +107,8 @@ Display the latest version of Wikity
|
|
|
78
107
|
Use [Wikitext](https://en.wikipedia.org/wiki/Help:Wikitext) (file extension `.wiki`) to create your pages.
|
|
79
108
|
|
|
80
109
|
Any wiki templates (called using `{{template name}}`) must be inside the `templates/` folder by default.
|
|
110
|
+
Any files must be inside the `images/` folder by default.
|
|
111
|
+
Your wikitext (`*.wiki`) files go in the root directory by default.
|
|
81
112
|
|
|
82
113
|
### Wiki markup
|
|
83
114
|
|
|
@@ -102,6 +133,7 @@ Any wiki templates (called using `{{template name}}`) must be inside the `templa
|
|
|
102
133
|
| `[[link\|display text]]` | [display text](#link) |
|
|
103
134
|
| `[external-link]` | [[1]](#external-link) |
|
|
104
135
|
| `[external-link display text]` | [display text](#external-link) |
|
|
136
|
+
| `[[File:Example.png\|Caption.]]` |  |
|
|
105
137
|
| `{{tp name}}` | *(contents of `templates/tp_name.wiki`)* |
|
|
106
138
|
| `{{tp name\|arg=val}}` | *(ditto but `{{{arg}}}` is set to 'val')* |
|
|
107
139
|
| `{{{arg}}}` | *(value given by template)* |
|
package/changelog.md
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 1.2.1
|
|
4
|
-
*2021-04-02*
|
|
5
|
-
- Added previously-unimplemented `config` option to `parse()` to configure the `templatesFolder`.
|
|
6
|
-
- Changed table of contents to not require default styling to collapse.
|
|
7
|
-
- Fixed internal links being incorrectly root-relative.
|
|
8
|
-
- Fixed templates output folder not being created on initialisation.
|
|
9
|
-
- Fixed unset arguments not being removed from the output.
|
|
10
|
-
|
|
11
|
-
## 1.2.0
|
|
12
|
-
*2021-04-01*
|
|
13
|
-
- Added function `eleventyPlugin()` for use with Eleventy's `addPlugin` method.
|
|
14
|
-
- Added configuration option `outputFolder` to configure the folder the compiled HTML files are placed in.
|
|
15
|
-
- Added configuration option `templatesFolder` to configure the folder templates are placed in.
|
|
16
|
-
- Added CLI options `--outputFolder`, `--templatesFolder`, `--eleventy`, and `--defaultStyles` to change configuration options.
|
|
17
|
-
- Added support for tables.
|
|
18
|
-
- Added a warning when the parser detects non-repetition-based `#time` function syntax is being used.
|
|
19
|
-
|
|
20
|
-
## 1.1.0
|
|
21
|
-
*2021-03-28*
|
|
22
|
-
- Added `parse` CLI command to implement `parse()`.
|
|
23
|
-
- Added a generated table of contents if there are over 4 headings.
|
|
24
|
-
- Added support for `nowiki` tag.
|
|
25
|
-
- Added support for `onlyinclude`, `includeonly`, and `noinclude` tags in templates.
|
|
26
|
-
- Added support for magic words `__TOC__`, `__FORCETOC__`, `__NOTOC__`, and `__NOINDEX__`.
|
|
27
|
-
- Added support for control function `{{displaytitle:}}` to control the page's displayed title.
|
|
28
|
-
- Added support for string functions `lc:`, `uc:`, `lcfirst:`, `ucfirst:`, `replace:`, `explode:`, `sub:`, `len:`, `pos:`, `padleft:`, `padright:`, `urlencode:`, and `urldecode:`.
|
|
29
|
-
- Added support for horizontal rules using `----`.
|
|
30
|
-
- Changed time codes in `#datetime`/`#date`/`#time` function to be based on reduplication instead of unique characters with escaping based on quoting instead of prefixing with a backslash (i.e., `{{#time: j F Y (\n\o\w)}}` → `{{#time: dd mmm yyyy "(now)"}}`).
|
|
31
|
-
- Fixed inline tags removing whitespace from either end.
|
|
32
|
-
- Fixed single-line-only syntax not being parsed correctly.
|
|
33
|
-
|
|
34
|
-
## 1.0.1
|
|
35
|
-
*2021-03-28*
|
|
36
|
-
- Changed HTML output to be prettified.
|
|
37
|
-
- Fixed arguments not being substituted properly.
|
|
38
|
-
- Fixed nested templates and parser functions not being substituted properly.
|
|
39
|
-
- Fixed templates and parser functions spread out across multiple lines not being parsed.
|
|
40
|
-
|
|
41
|
-
## 1.0.0
|
|
42
|
-
*2021-03-27*
|
|
43
|
-
- Added `compile()` function and CLI command to compile wikitext into HTML.
|
|
44
|
-
- Added `parse()` function to parse raw wikitext input.
|
package/src/cli.d.ts
DELETED
package/src/cli.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
-
};
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const index_1 = __importDefault(require("./index"));
|
|
8
|
-
const VERSION = '1.2.1';
|
|
9
|
-
const indent = (n) => ' '.repeat(n * 4);
|
|
10
|
-
const usage = (command, ...desc) => {
|
|
11
|
-
console.log('\n' + indent(2) + command);
|
|
12
|
-
desc.forEach((msg) => console.log(indent(2.5) + msg));
|
|
13
|
-
};
|
|
14
|
-
const arg = (n) => process.argv[n + 1] || '';
|
|
15
|
-
const args = process.argv.slice(1);
|
|
16
|
-
if (!arg(1)) {
|
|
17
|
-
console.log('Type `wikity help` for a list of commands.');
|
|
18
|
-
}
|
|
19
|
-
else if (arg(1).includes('h')) {
|
|
20
|
-
console.log(`\n${indent(1)}Wikity CLI commands:`);
|
|
21
|
-
usage(`wikity (help|-h)`, `Display this help message.`);
|
|
22
|
-
usage(`wikity compile [<folder>] [-o <folder>] [-t <folder>] [-e] [-d]`, `Compile wikitext files from a given folder.`, ` [<folder>]\n${indent(3.5)}Input folder ('.' (current folder) if unset).`, ` (-o|--outputFolder) <folder>\n${indent(3.5)}Folder that compiled HTML files are placed in ('wikity-out' if unset).`, ` (-t|--templatesFolder) <folder>\n${indent(3.5)}Where to place wiki templates ('templates' if unset).`, ` (-e|--eleventy)\n${indent(3.5)}Compiles files with Eleventy front matter (false if unset).`, ` (-d|--defaultStyles)\n${indent(3.5)}Add default wiki styling to all pages (true if unset).`);
|
|
23
|
-
usage(`wikity (parse|-p) "<input>"`, `Parse raw wikitext from the command line.`);
|
|
24
|
-
usage(`wikity (version|-v)`, `Display the current version of Wikity.`);
|
|
25
|
-
}
|
|
26
|
-
else if (arg(1).includes('c')) {
|
|
27
|
-
const configArgs = args.slice(2);
|
|
28
|
-
const folder = arg(2) || '.';
|
|
29
|
-
const outputFolder = configArgs.join(' ').includes('-o') && configArgs.filter((_, i) => { var _a; return (_a = configArgs[i - 1]) === null || _a === void 0 ? void 0 : _a.includes('-o'); }).join(' ') || '';
|
|
30
|
-
const templatesFolder = configArgs.join(' ').includes('-t') && configArgs.filter((_, i) => { var _a; return (_a = configArgs[i - 1]) === null || _a === void 0 ? void 0 : _a.includes('-t'); }).join(' ') || '';
|
|
31
|
-
const eleventy = configArgs.join(' ').includes('-e');
|
|
32
|
-
const defaultStyles = configArgs.join(' ').includes('-d');
|
|
33
|
-
index_1.default.compile(folder, { outputFolder, templatesFolder, eleventy, defaultStyles });
|
|
34
|
-
}
|
|
35
|
-
else if (arg(1).includes('p')) {
|
|
36
|
-
const input = arg(2);
|
|
37
|
-
console.log(index_1.default.parse(input));
|
|
38
|
-
}
|
|
39
|
-
else if (arg(1).includes('v')) {
|
|
40
|
-
console.log('The current version of Wikity is ' + VERSION);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
console.log('Unknown command; type `wikity help` for help');
|
|
44
|
-
}
|
package/src/common.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export declare type Metadata = Record<string, any>;
|
|
2
|
-
export declare type Config = {
|
|
3
|
-
outputFolder?: string;
|
|
4
|
-
eleventy?: boolean;
|
|
5
|
-
defaultStyles?: boolean;
|
|
6
|
-
customStyles?: string;
|
|
7
|
-
templatesFolder?: string;
|
|
8
|
-
};
|
|
9
|
-
export declare class Result extends String {
|
|
10
|
-
metadata: Metadata;
|
|
11
|
-
constructor(str: string);
|
|
12
|
-
}
|
|
13
|
-
export declare function RegExpBuilder(regex: string, flag?: string): RegExp;
|
package/src/compile.js
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.compile = exports.eleventyCompile = void 0;
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const glob = require('glob');
|
|
6
|
-
const dedent = require('dedent');
|
|
7
|
-
const formatter = require('html-formatter');
|
|
8
|
-
const parse_1 = require("./parse");
|
|
9
|
-
const common_1 = require("./common");
|
|
10
|
-
const r = String.raw;
|
|
11
|
-
function eleventyCompile(dir = '.', config = {}) {
|
|
12
|
-
compile(dir, Object.assign({ eleventy: true }, config));
|
|
13
|
-
}
|
|
14
|
-
exports.eleventyCompile = eleventyCompile;
|
|
15
|
-
function compile(dir = '.', config = {}) {
|
|
16
|
-
let stylesCreated = false;
|
|
17
|
-
// Write wikitext files
|
|
18
|
-
const files = glob.sync((dir || '.') + "/**/*.wiki", {});
|
|
19
|
-
files.forEach((file) => {
|
|
20
|
-
var _a;
|
|
21
|
-
let data = fs.readFileSync(file, { encoding: 'utf8' });
|
|
22
|
-
let content = parse_1.parse(data, config);
|
|
23
|
-
let outText = content.toString();
|
|
24
|
-
const templatesFolder = config.templatesFolder || 'templates';
|
|
25
|
-
let [, folder, filename] = file.match(common_1.RegExpBuilder(r `^(.+?[\/\\]) ((?:${templatesFolder}[\/\\])?[^\/\\]+)$`, ''));
|
|
26
|
-
let outFolder = (dir || folder || '.') + '/' + (config.outputFolder || 'wikity-out') + '/';
|
|
27
|
-
let outFilename = filename.replace(/ /g, '_').replace('.wiki', '.html');
|
|
28
|
-
let url = outFilename.replace(/(?<=^|\/)\w/g, m => m.toUpperCase());
|
|
29
|
-
let displayTitle = content.metadata.displayTitle || url.replace('.html', '');
|
|
30
|
-
// Eleventy configuration
|
|
31
|
-
let frontMatter = '';
|
|
32
|
-
if (config.eleventy) {
|
|
33
|
-
frontMatter = dedent `
|
|
34
|
-
---
|
|
35
|
-
permalink: /wiki/${url}
|
|
36
|
-
---
|
|
37
|
-
`;
|
|
38
|
-
}
|
|
39
|
-
// Create HTML
|
|
40
|
-
let toc = '';
|
|
41
|
-
if (!content.metadata.notoc && (content.metadata.toc || (((_a = outText.match(/<h\d[^>]*>/g)) === null || _a === void 0 ? void 0 : _a.length) || 0) > 3)) {
|
|
42
|
-
let headings = Array.from(content.match(/<h\d[^>]*>.+?<\/h\d>/gs) || []);
|
|
43
|
-
headings.forEach(match => {
|
|
44
|
-
var _a;
|
|
45
|
-
const text = match.replace(/\s*<\/?h\d[^>]*>\s*/g, '');
|
|
46
|
-
const lvl = +(((_a = match.match(/\d/g)) === null || _a === void 0 ? void 0 : _a[0]) || -1);
|
|
47
|
-
toc += `${`<ol>`.repeat(lvl - 1)} <li> <a href="#${encodeURI(text.replace(/ /g, '_'))}">${text}</a> </li> ${`</ol>`.repeat(lvl - 1)}`;
|
|
48
|
-
});
|
|
49
|
-
toc = dedent `
|
|
50
|
-
<div id="toc">
|
|
51
|
-
<span id="toc-heading">
|
|
52
|
-
<strong>Contents</strong>
|
|
53
|
-
[<a href="javascript:void(0)" onclick="
|
|
54
|
-
document.querySelector('#toc ol').setAttribute('style', this.innerText === 'hide' ? 'display: none;' : '');
|
|
55
|
-
this.innerText = this.innerText === 'hide' ? 'show' : 'hide';
|
|
56
|
-
">hide</a>]
|
|
57
|
-
</span>
|
|
58
|
-
<ol>${toc}</ol>
|
|
59
|
-
</div>
|
|
60
|
-
`;
|
|
61
|
-
if (outText.includes('<toc></toc>'))
|
|
62
|
-
outText = outText.replace('<toc></toc>', toc);
|
|
63
|
-
else
|
|
64
|
-
outText = outText.replace(/<h\d[^>]*>/, toc + '$&');
|
|
65
|
-
}
|
|
66
|
-
let html = dedent `
|
|
67
|
-
<html>
|
|
68
|
-
<head>
|
|
69
|
-
<meta charset="utf-8">
|
|
70
|
-
<meta name="viewport" content="initial-scale=1.0, width=device-width">
|
|
71
|
-
<meta name="description" content="${data.substr(0, 256)}">
|
|
72
|
-
<title>${displayTitle}</title>
|
|
73
|
-
<link id="default-styles" rel="stylesheet" href="/wiki.css">
|
|
74
|
-
</head>
|
|
75
|
-
<body>
|
|
76
|
-
<header>
|
|
77
|
-
<h1 id="page-title">${displayTitle}</h1>
|
|
78
|
-
</header>
|
|
79
|
-
<main>
|
|
80
|
-
<p>\n${outText}
|
|
81
|
-
</p>
|
|
82
|
-
</main>
|
|
83
|
-
<footer>
|
|
84
|
-
<p id="credit_wikity">Created using <a href="https://github.com/Nixinova/Wikity">Wikity</a></p>
|
|
85
|
-
</footer>
|
|
86
|
-
</body>
|
|
87
|
-
</html>
|
|
88
|
-
`;
|
|
89
|
-
// Write to file
|
|
90
|
-
if (!fs.existsSync(outFolder)) {
|
|
91
|
-
fs.mkdirSync(outFolder);
|
|
92
|
-
fs.mkdirSync(outFolder + templatesFolder + '/');
|
|
93
|
-
}
|
|
94
|
-
let renderedHtml = formatter.render(html).replace(/(<\/\w+>)(\S)/g, '$1 $2');
|
|
95
|
-
fs.writeFileSync(outFolder + outFilename, frontMatter + '\n' + renderedHtml, 'utf8');
|
|
96
|
-
// Create site files
|
|
97
|
-
if (stylesCreated)
|
|
98
|
-
return;
|
|
99
|
-
stylesCreated = true;
|
|
100
|
-
let styles = '';
|
|
101
|
-
if (config.defaultStyles !== false) {
|
|
102
|
-
styles += dedent `
|
|
103
|
-
body {font-family: sans-serif; margin: 4em; max-width: 1000px; background: #eee;}
|
|
104
|
-
main {margin: 3em -1em; background: #fff; padding: 1em;}
|
|
105
|
-
h1, h2 {margin-bottom: 0.6em; font-weight: normal; border-bottom: 1px solid #a2a9b1;}
|
|
106
|
-
ul, ol {margin: 0.3em 0 0 1.6em; padding: 0;}
|
|
107
|
-
dt {font-weight: bold;}
|
|
108
|
-
dd, dl dl {margin-block: 0; margin-inline-start: 30px;}
|
|
109
|
-
|
|
110
|
-
a:not(:hover) {text-decoration: none;}
|
|
111
|
-
a.internal-link {color: #04a;}
|
|
112
|
-
a.internal-link:visited {color: #26d;}
|
|
113
|
-
a.external-link {color: #36b;}
|
|
114
|
-
a.external-link:visited {color: #58d;}
|
|
115
|
-
a.external-link::after {content: '\1f855';}
|
|
116
|
-
a.redlink {color: #d33;}
|
|
117
|
-
a.redlink:visited {color: #b44;}
|
|
118
|
-
|
|
119
|
-
#toc {display: inline-block; border: 1px solid #aab; padding: 8px; background-color: #f8f8f8; font-size: 95%;}
|
|
120
|
-
#toc-heading {display: block; text-align: center;}
|
|
121
|
-
#toc ol {margin: 0 0 0 1.3em;}
|
|
122
|
-
`;
|
|
123
|
-
}
|
|
124
|
-
if (config.customStyles)
|
|
125
|
-
styles += config.customStyles;
|
|
126
|
-
let cssOutput = dedent `
|
|
127
|
-
---
|
|
128
|
-
permalink: /wiki.css
|
|
129
|
-
---
|
|
130
|
-
${styles}
|
|
131
|
-
`;
|
|
132
|
-
fs.writeFileSync(outFolder + 'wiki.css.njk', cssOutput);
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
exports.compile = compile;
|
package/src/index.js
DELETED
package/src/parse.js
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parse = exports.rawParse = void 0;
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const htmlEscape = require('escape-html');
|
|
6
|
-
const dateFormat = require('dateformat');
|
|
7
|
-
const common_1 = require("./common");
|
|
8
|
-
const r = String.raw;
|
|
9
|
-
const MAX_RECURSION = 20;
|
|
10
|
-
const arg = r `\s*([^|}]+?)\s*`;
|
|
11
|
-
function rawParse(data, config = {}) {
|
|
12
|
-
return parse(data, config).toString();
|
|
13
|
-
}
|
|
14
|
-
exports.rawParse = rawParse;
|
|
15
|
-
function parse(data, config = {}) {
|
|
16
|
-
const vars = {};
|
|
17
|
-
const metadata = {};
|
|
18
|
-
let nowikis = [];
|
|
19
|
-
let nowikiCount = 0;
|
|
20
|
-
let rawExtLinkCount = 0;
|
|
21
|
-
let refCount = 0;
|
|
22
|
-
let refs = [];
|
|
23
|
-
let outText = data;
|
|
24
|
-
for (let l = 0, last = ''; l < MAX_RECURSION; l++) {
|
|
25
|
-
if (last === outText)
|
|
26
|
-
break;
|
|
27
|
-
last = outText;
|
|
28
|
-
outText = outText
|
|
29
|
-
// Nowiki: <nowiki></nowiki>
|
|
30
|
-
.replace(common_1.RegExpBuilder(r `<nowiki> ([^]+?) </nowiki>`), (_, m) => `%NOWIKI#${nowikis.push(m), nowikiCount++}%`)
|
|
31
|
-
// Sanitise unacceptable HTML
|
|
32
|
-
.replace(common_1.RegExpBuilder(r `<(/?) \s* (?= script|link|meta|iframe|frameset|object|embed|applet|form|input|button|textarea )`), '<$1')
|
|
33
|
-
.replace(common_1.RegExpBuilder(r `(?<= <[^>]+ ) (\bon(\w+))`), 'data-$2')
|
|
34
|
-
// Comments: <!-- -->
|
|
35
|
-
.replace(/<!--[^]+?-->/g, '')
|
|
36
|
-
// Lines: ----
|
|
37
|
-
.replace(/^-{4,}/gm, '<hr>')
|
|
38
|
-
// Metadata: displayTitle, __NOTOC__, etc
|
|
39
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* displayTitle: ([^}]+) }}`), (_, title) => (metadata.displayTitle = title, ''))
|
|
40
|
-
.replace(common_1.RegExpBuilder(r `__NOINDEX__`), () => (metadata.noindex = true, ''))
|
|
41
|
-
.replace(common_1.RegExpBuilder(r `__NOTOC__`), () => (metadata.notoc = true, ''))
|
|
42
|
-
.replace(common_1.RegExpBuilder(r `__FORCETOC__`), () => (metadata.toc = true, ''))
|
|
43
|
-
.replace(common_1.RegExpBuilder(r `__TOC__`), () => (metadata.toc = true, `<toc></toc>`))
|
|
44
|
-
// Magic words: {{!}}, {{reflist}}, etc
|
|
45
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* ! \s* }}`), '|')
|
|
46
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* = \s* }}`), '=')
|
|
47
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* [Rr]eflist \s* }}`), '<references/>')
|
|
48
|
-
// String functions: {{lc:}}, {{ucfirst:}}, {{len:}}, etc
|
|
49
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? urlencode: ${arg} }}`), (_, m) => encodeURI(m))
|
|
50
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? urldecode: ${arg} }}`), (_, m) => decodeURI(m))
|
|
51
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? lc: ${arg} }}`), (_, m) => m.toLowerCase())
|
|
52
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? uc: ${arg} }}`), (_, m) => m.toUpperCase())
|
|
53
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? lcfirst: ${arg} }}`), (_, m) => m[0].toLowerCase() + m.substr(1))
|
|
54
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? ucfirst: ${arg} }}`), (_, m) => m[0].toUpperCase() + m.substr(1))
|
|
55
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? len: ${arg} }}`), (_, m) => m.length)
|
|
56
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? pos: ${arg} \|${arg} (?: \s*\|${arg} )? }}`), (_, find, str, n = 0) => find.substr(n).indexOf(str))
|
|
57
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? sub: ${arg} \|${arg} (?:\|${arg})? }}`), (_, str, from, len) => str.substr(+from - 1, +len))
|
|
58
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? padleft: ${arg} \|${arg} \|${arg} }}`), (_, str, n, char) => str.padStart(+n, char))
|
|
59
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? padright: ${arg} \|${arg} \|${arg} }}`), (_, str, n, char) => str.padEnd(+n, char))
|
|
60
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? replace: ${arg} \|${arg} \|${arg} }}`), (_, str, find, rep) => str.split(find).join(rep))
|
|
61
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* #? explode: ${arg} \|${arg} \|${arg} }}`), (_, str, delim, pos) => str.split(delim)[+pos])
|
|
62
|
-
// Parser functions: {{#if:}}, {{#switch:}}, etc
|
|
63
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* (#\w+) \s* : \s* ( [^{}]+ ) \s* }} ( ?!} )`), (_, name, content) => {
|
|
64
|
-
if (/{{\s*#/.test(content))
|
|
65
|
-
return _;
|
|
66
|
-
const args = content.trim().split(/\s*\|\s*/);
|
|
67
|
-
switch (name) {
|
|
68
|
-
case '#if':
|
|
69
|
-
return (args[0] ? args[1] : args[2]) || '';
|
|
70
|
-
case '#ifeq':
|
|
71
|
-
return (args[0] === args[1] ? args[2] : args[3]) || '';
|
|
72
|
-
case '#vardefine':
|
|
73
|
-
vars[args[0]] = args[1] || '';
|
|
74
|
-
return '';
|
|
75
|
-
case '#var':
|
|
76
|
-
if (common_1.RegExpBuilder(r `{{ \s* #vardefine \s* : \s* ${args[0]}`).test(outText))
|
|
77
|
-
return _; // wait until var is set
|
|
78
|
-
return vars[args[0]] || args[1] || '';
|
|
79
|
-
case '#switch':
|
|
80
|
-
return args.slice(1)
|
|
81
|
-
.map(arg => arg.split(/\s*=\s*/))
|
|
82
|
-
.filter(duo => args[0] === duo[0].replace('#default', args[0]))[0][1];
|
|
83
|
-
case '#time':
|
|
84
|
-
case '#date':
|
|
85
|
-
case '#datetime':
|
|
86
|
-
// make sure the characters are not inside a string
|
|
87
|
-
let parsedMatch = args[0].replace(/".+?"/g, '').replace(/'.+?'/g, '');
|
|
88
|
-
if (/[abcefgijkqruvx]/i.test(parsedMatch)) {
|
|
89
|
-
console.warn(`<Wikity> [WARN] Wikity does not use Wikipedia's #time function syntax. Use repetition-based formatting instead.`);
|
|
90
|
-
}
|
|
91
|
-
return dateFormat(args[1] ? new Date(args[1]) : new Date(), args[0]);
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
// Templates: {{template}}
|
|
95
|
-
.replace(common_1.RegExpBuilder(r `{{ \s* ([^#}|]+?) (\|[^}]+)? }} (?!})`), (_, title, params = '') => {
|
|
96
|
-
if (/{{/.test(params))
|
|
97
|
-
return _;
|
|
98
|
-
const page = (config.templatesFolder || 'templates') + '/' + title.trim().replace(/ /g, '_');
|
|
99
|
-
// Retrieve template content
|
|
100
|
-
let content = '';
|
|
101
|
-
try {
|
|
102
|
-
content = fs.readFileSync('./' + page + '.wiki', { encoding: 'utf8' });
|
|
103
|
-
}
|
|
104
|
-
catch (_a) {
|
|
105
|
-
return `<a class="internal-link redlink" title="${title}" href="${page}">${title}</a>`;
|
|
106
|
-
}
|
|
107
|
-
// Remove non-template sections
|
|
108
|
-
content = content
|
|
109
|
-
.replace(/<noinclude>.*?<\/noinclude>/gs, '')
|
|
110
|
-
.replace(/.*<(includeonly|onlyinclude)>|<\/(includeonly|onlyinclude)>.*/gs, '');
|
|
111
|
-
// Substitite arguments
|
|
112
|
-
const argMatch = (arg) => common_1.RegExpBuilder(r `{{{ \s* ${arg} (?:\|([^}]*))? \s* }}}`);
|
|
113
|
-
let args = params.split('|').slice(1);
|
|
114
|
-
for (let i in args) {
|
|
115
|
-
let parts = args[i].split('=');
|
|
116
|
-
let [arg, val] = parts[1] ? [parts[0], ...parts.slice(1)] : [(+i + 1) + '', parts[0]];
|
|
117
|
-
content = content.replace(argMatch(arg), (_, m) => val || m || '');
|
|
118
|
-
}
|
|
119
|
-
for (let i = 1; i <= 10; i++) {
|
|
120
|
-
content = content.replace(argMatch(arg), '$2');
|
|
121
|
-
}
|
|
122
|
-
return content;
|
|
123
|
-
})
|
|
124
|
-
// Markup: '''bold''' and '''italic'''
|
|
125
|
-
.replace(common_1.RegExpBuilder(r `''' ([^']+?) '''`), '<b>$1</b>')
|
|
126
|
-
.replace(common_1.RegExpBuilder(r `'' ([^']+?) ''`), '<i>$1</i>')
|
|
127
|
-
// Headings: ==heading==
|
|
128
|
-
.replace(common_1.RegExpBuilder(r `^ (=+) \s* (.+?) \s* \1 \s* $`), (_, lvl, txt) => `<h${lvl.length} id="${encodeURI(txt.replace(/ /g, '_'))}">${txt}</h${lvl.length}>`)
|
|
129
|
-
// Internal links: [[Page]] and [[Page|Text]]
|
|
130
|
-
.replace(common_1.RegExpBuilder(r `\[\[ ([^\]|]+?) \]\]`), `<a class="internal-link" title="$1" href="$1">$1</a>`)
|
|
131
|
-
.replace(common_1.RegExpBuilder(r `\[\[ ([^\]|]+?) \| ([^\]]+?) \]\]`), `<a class="internal-link" title="$1" href="/$1">$2</a>`)
|
|
132
|
-
.replace(common_1.RegExpBuilder(r `(</a>)([a-z]+)`), '$2$1')
|
|
133
|
-
// External links: [href Page] and just [href]
|
|
134
|
-
.replace(common_1.RegExpBuilder(r `\[ ((?:\w+:)?\/\/ [^\s\]]+) (\s [^\]]+?)? \]`), (_, href, txt) => `<a class="external-link" href="${href}">${txt || '[' + (++rawExtLinkCount) + ']'}</a>`)
|
|
135
|
-
// Bulleted list: *item
|
|
136
|
-
.replace(common_1.RegExpBuilder(r `^ (\*+) (.+?) $`), (_, lvl, txt) => `${'<ul>'.repeat(lvl.length)}<li>${txt}</li>${'</ul>'.repeat(lvl.length)}`)
|
|
137
|
-
.replace(common_1.RegExpBuilder(r `</ul> (\s*?) <ul>`), '$1')
|
|
138
|
-
// Numbered list: #item
|
|
139
|
-
.replace(common_1.RegExpBuilder(r `^ (#+) (.+?) $`), (_, lvl, txt) => `${'<ol>'.repeat(lvl.length)}<li>${txt}</li>${'</ol>'.repeat(lvl.length)}`)
|
|
140
|
-
.replace(common_1.RegExpBuilder(r `</ol> (\s*?) <ol>`), '$1')
|
|
141
|
-
// Definition list: ;head, :item
|
|
142
|
-
.replace(common_1.RegExpBuilder(r `^ ; (.+) $`), '<dl><dt>$1</dt></dl>')
|
|
143
|
-
.replace(common_1.RegExpBuilder(r `^ (:+) (.+?) $`), (_, lvl, txt) => `${'<dl>'.repeat(lvl.length)}<dd>${txt}</dd>${'</dl>'.repeat(lvl.length)}`)
|
|
144
|
-
.replace(common_1.RegExpBuilder(r `</dl> (\s*?) <dl>`), '$1')
|
|
145
|
-
// Tables: {|, |, |-, |}
|
|
146
|
-
.replace(common_1.RegExpBuilder(r `^ \{\| (.*?) $`), (_, attrs) => `<table ${attrs}><tr>`)
|
|
147
|
-
.replace(common_1.RegExpBuilder(r `^ ! ([^]+?) (?= \n^[!|] )`), (_, content) => `<th>${content}</th>`)
|
|
148
|
-
.replace(common_1.RegExpBuilder(r `^ \|[^-}] ([^]*?) (?= \n^[!|] )`), (_, content) => `<td>${content}</td>`)
|
|
149
|
-
.replace(common_1.RegExpBuilder(r `^ \|- (.*?) $`), (_, attrs) => `</tr><tr ${attrs}>`)
|
|
150
|
-
.replace(common_1.RegExpBuilder(r `^ \|\}`), `</tr></table>`)
|
|
151
|
-
// References: <ref></ref>, <references/>
|
|
152
|
-
.replace(common_1.RegExpBuilder(r `<ref> (.+?) </ref>`), (_, text) => {
|
|
153
|
-
refs.push(text);
|
|
154
|
-
refCount++;
|
|
155
|
-
return `<sup><a id="cite-${refCount}" class="ref" href="#ref-${refCount}">[${refCount}]</a></sup>`;
|
|
156
|
-
})
|
|
157
|
-
.replace(common_1.RegExpBuilder(r `<references \s* /?>`), '<ol>' + refs.map((ref, i) => `<li id="ref-${+i + 1}"> <a href="#cite-${+i + 1}">↑</a> ${ref} </li>`).join('\n') + '</ol>')
|
|
158
|
-
// Nonstandard: ``code`` and ```code blocks```
|
|
159
|
-
.replace(common_1.RegExpBuilder(r ` \`\`\` ([^\`]+?) \`\`\` `), '<pre>$1</pre>')
|
|
160
|
-
.replace(common_1.RegExpBuilder(r ` \`\` ([^\`]+?) \`\` `), '<code>$1</code>')
|
|
161
|
-
// Spacing
|
|
162
|
-
.replace(/(\r?\n){2}/g, '\n</p><p>\n')
|
|
163
|
-
// Restore nowiki contents
|
|
164
|
-
.replace(/%NOWIKI#(\d+)%/g, (_, n) => htmlEscape(nowikis[n]));
|
|
165
|
-
}
|
|
166
|
-
let result = new common_1.Result(outText);
|
|
167
|
-
result.metadata = metadata;
|
|
168
|
-
return result;
|
|
169
|
-
}
|
|
170
|
-
exports.parse = parse;
|