mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-19 14:13:34 +01:00
Ported bin folder to typescript.
This commit is contained in:
parent
a768b322cf
commit
f9e3416d78
24 changed files with 346 additions and 1123 deletions
|
@ -10,14 +10,14 @@ process.on('unhandledRejection', (err) => { throw err; });
|
||||||
if (process.argv.length !== 2) throw new Error('Use: node bin/checkAllPads.js');
|
if (process.argv.length !== 2) throw new Error('Use: node bin/checkAllPads.js');
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const db = require('./src/node/db/DB');
|
const db = require('ep_etherpad-lite/node/db/DB');
|
||||||
await db.init();
|
await db.init();
|
||||||
const padManager = require('./src/node/db/PadManager');
|
const padManager = require('ep_etherpad-lite/node/db/PadManager');
|
||||||
await Promise.all((await padManager.listAllPads()).padIDs.map(async (padId) => {
|
await Promise.all((await padManager.listAllPads()).padIDs.map(async (padId: string) => {
|
||||||
const pad = await padManager.getPad(padId);
|
const pad = await padManager.getPad(padId);
|
||||||
try {
|
try {
|
||||||
await pad.check();
|
await pad.check();
|
||||||
} catch (err) {
|
} catch (err:any) {
|
||||||
console.error(`Error in pad ${padId}: ${err.stack || err}`);
|
console.error(`Error in pad ${padId}: ${err.stack || err}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
|
@ -8,11 +8,12 @@
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
process.on('unhandledRejection', (err) => { throw err; });
|
||||||
|
|
||||||
if (process.argv.length !== 3) throw new Error('Use: node bin/checkPad.js $PADID');
|
if (process.argv.length !== 3) throw new Error('Use: node bin/checkPad.js $PADID');
|
||||||
|
// @ts-ignore
|
||||||
const padId = process.argv[2];
|
const padId = process.argv[2];
|
||||||
(async () => {
|
(async () => {
|
||||||
const db = require('./src/node/db/DB');
|
const db = require('ep_etherpad-lite/node/db/DB');
|
||||||
await db.init();
|
await db.init();
|
||||||
const padManager = require('./src/node/db/PadManager');
|
const padManager = require('ep_etherpad-lite/node/db/PadManager');
|
||||||
if (!await padManager.doesPadExists(padId)) throw new Error('Pad does not exist');
|
if (!await padManager.doesPadExists(padId)) throw new Error('Pad does not exist');
|
||||||
const pad = await padManager.getPad(padId);
|
const pad = await padManager.getPad(padId);
|
||||||
await pad.check();
|
await pad.check();
|
|
@ -7,16 +7,20 @@
|
||||||
|
|
||||||
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
||||||
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
import querystring from "querystring";
|
||||||
|
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
process.on('unhandledRejection', (err) => { throw err; });
|
||||||
|
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const querystring = require('querystring');
|
|
||||||
const settings = require('./src/node/utils/Settings');
|
|
||||||
const supertest = require('supertest');
|
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const api = supertest(`http://${settings.ip}:${settings.port}`);
|
axios.defaults.baseURL = `http://${settings.ip}:${settings.port}`;
|
||||||
|
const api = axios;
|
||||||
|
|
||||||
const filePath = path.join(__dirname, '../APIKEY.txt');
|
const filePath = path.join(__dirname, '../APIKEY.txt');
|
||||||
const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
||||||
|
@ -24,28 +28,29 @@ const supertest = require('supertest');
|
||||||
let res;
|
let res;
|
||||||
|
|
||||||
res = await api.get('/api/');
|
res = await api.get('/api/');
|
||||||
const apiVersion = res.body.currentVersion;
|
const apiVersion = res.data.currentVersion;
|
||||||
if (!apiVersion) throw new Error('No version set in API');
|
if (!apiVersion) throw new Error('No version set in API');
|
||||||
const uri = (cmd, args) => `/api/${apiVersion}/${cmd}?${querystring.stringify(args)}`;
|
console.log('apiVersion', apiVersion);
|
||||||
|
const uri = (cmd: string, args: querystring.ParsedUrlQueryInput ) => `/api/${apiVersion}/${cmd}?${querystring.stringify(args)}`;
|
||||||
|
|
||||||
res = await api.post(uri('createGroup', {apikey}));
|
res = await api.post(uri('createGroup', {apikey}));
|
||||||
if (res.body.code === 1) throw new Error(`Error creating group: ${res.body}`);
|
if (res.data.code === 1) throw new Error(`Error creating group: ${res.data}`);
|
||||||
const groupID = res.body.data.groupID;
|
const groupID = res.data.data.groupID;
|
||||||
console.log('groupID', groupID);
|
console.log('groupID', groupID);
|
||||||
|
|
||||||
res = await api.post(uri('createGroupPad', {apikey, groupID}));
|
res = await api.post(uri('createGroupPad', {apikey, groupID}));
|
||||||
if (res.body.code === 1) throw new Error(`Error creating group pad: ${res.body}`);
|
if (res.data.code === 1) throw new Error(`Error creating group pad: ${res.data}`);
|
||||||
console.log('Test Pad ID ====> ', res.body.data.padID);
|
console.log('Test Pad ID ====> ', res.data.data.padID);
|
||||||
|
|
||||||
res = await api.post(uri('createAuthor', {apikey}));
|
res = await api.post(uri('createAuthor', {apikey}));
|
||||||
if (res.body.code === 1) throw new Error(`Error creating author: ${res.body}`);
|
if (res.data.code === 1) throw new Error(`Error creating author: ${res.data}`);
|
||||||
const authorID = res.body.data.authorID;
|
const authorID = res.data.data.authorID;
|
||||||
console.log('authorID', authorID);
|
console.log('authorID', authorID);
|
||||||
|
|
||||||
const validUntil = Math.floor(new Date() / 1000) + 60000;
|
const validUntil = Math.floor(new Date().getTime() / 1000) + 60000;
|
||||||
console.log('validUntil', validUntil);
|
console.log('validUntil', validUntil);
|
||||||
res = await api.post(uri('createSession', {apikey, groupID, authorID, validUntil}));
|
res = await api.post(uri('createSession', {apikey, groupID, authorID, validUntil}));
|
||||||
if (res.body.code === 1) throw new Error(`Error creating session: ${res.body}`);
|
if (res.data.code === 1) throw new Error(`Error creating session: ${JSON.stringify(res.data)}`);
|
||||||
console.log('Session made: ====> create a cookie named sessionID and set the value to',
|
console.log('Session made: ====> create a cookie named sessionID and set the value to',
|
||||||
res.body.data.sessionID);
|
res.data.data.sessionID);
|
||||||
})();
|
})();
|
|
@ -1,5 +1,3 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A tool for deleting ALL GROUP sessions Etherpad user sessions from the CLI,
|
* A tool for deleting ALL GROUP sessions Etherpad user sessions from the CLI,
|
||||||
* because sometimes a brick is required to fix a face.
|
* because sometimes a brick is required to fix a face.
|
||||||
|
@ -7,12 +5,13 @@
|
||||||
|
|
||||||
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
||||||
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
import fs from "fs";
|
||||||
|
import process from "process";
|
||||||
|
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
process.on('unhandledRejection', (err) => { throw err; });
|
||||||
|
import axios from 'axios'
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs');
|
|
||||||
const supertest = require('supertest');
|
|
||||||
|
|
||||||
// Set a delete counter which will increment on each delete attempt
|
// Set a delete counter which will increment on each delete attempt
|
||||||
// TODO: Check delete is successful before incrementing
|
// TODO: Check delete is successful before incrementing
|
||||||
let deleteCount = 0;
|
let deleteCount = 0;
|
||||||
|
@ -20,27 +19,33 @@ let deleteCount = 0;
|
||||||
// get the API Key
|
// get the API Key
|
||||||
const filePath = path.join(__dirname, '../APIKEY.txt');
|
const filePath = path.join(__dirname, '../APIKEY.txt');
|
||||||
console.log('Deleting all group sessions, please be patient.');
|
console.log('Deleting all group sessions, please be patient.');
|
||||||
|
const settings = require('ep_etherpad-lite/tests/container/loadSettings').loadSettings();
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const settings = require('./src/tests/container/loadSettings').loadSettings();
|
|
||||||
const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
||||||
const api = supertest(`http://${settings.ip}:${settings.port}`);
|
axios.defaults.baseURL = `http://${settings.ip}:${settings.port}`;
|
||||||
|
|
||||||
const apiVersionResponse = await api.get('/api/');
|
const apiVersionResponse = await axios.get('/api/');
|
||||||
const apiVersion = apiVersionResponse.body.currentVersion; // 1.12.5
|
const apiVersion = apiVersionResponse.data.currentVersion; // 1.12.5
|
||||||
|
console.log('apiVersion', apiVersion);
|
||||||
|
|
||||||
const groupsResponse = await api.get(`/api/${apiVersion}/listAllGroups?apikey=${apikey}`);
|
const groupsResponse = await axios.get(`/api/${apiVersion}/listAllGroups?apikey=${apikey}`);
|
||||||
const groups = groupsResponse.body.data.groupIDs; // ['whateverGroupID']
|
const groups = groupsResponse.data.data.groupIDs; // ['whateverGroupID']
|
||||||
|
|
||||||
for (const groupID of groups) {
|
for (const groupID of groups) {
|
||||||
const sessionURI = `/api/${apiVersion}/listSessionsOfGroup?apikey=${apikey}&groupID=${groupID}`;
|
const sessionURI = `/api/${apiVersion}/listSessionsOfGroup?apikey=${apikey}&groupID=${groupID}`;
|
||||||
const sessionsResponse = await api.get(sessionURI);
|
const sessionsResponse = await axios.get(sessionURI);
|
||||||
const sessions = sessionsResponse.body.data;
|
const sessions = sessionsResponse.data.data;
|
||||||
|
|
||||||
for (const sessionID of Object.keys(sessions)) {
|
if(sessions == null) continue;
|
||||||
|
|
||||||
|
for (const [sessionID, val] of Object.entries(sessions)) {
|
||||||
|
if(val == null) continue;
|
||||||
const deleteURI = `/api/${apiVersion}/deleteSession?apikey=${apikey}&sessionID=${sessionID}`;
|
const deleteURI = `/api/${apiVersion}/deleteSession?apikey=${apikey}&sessionID=${sessionID}`;
|
||||||
await api.post(deleteURI); // delete
|
await axios.post(deleteURI).then(c=>{
|
||||||
|
console.log(c.data)
|
||||||
deleteCount++;
|
deleteCount++;
|
||||||
|
}); // delete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(`Deleted ${deleteCount} sessions`);
|
console.log(`Deleted ${deleteCount} sessions`);
|
|
@ -7,14 +7,17 @@
|
||||||
|
|
||||||
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
||||||
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
import fs from "fs";
|
||||||
|
import process from "process";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
process.on('unhandledRejection', (err) => { throw err; });
|
||||||
|
|
||||||
const settings = require('./src/tests/container/loadSettings').loadSettings();
|
const settings = require('ep_etherpad-lite/tests/container/loadSettings').loadSettings();
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs');
|
|
||||||
const supertest = require('supertest');
|
|
||||||
|
|
||||||
const api = supertest(`http://${settings.ip}:${settings.port}`);
|
axios.defaults.baseURL = `http://${settings.ip}:${settings.port}`;
|
||||||
|
|
||||||
if (process.argv.length !== 3) throw new Error('Use: node deletePad.js $PADID');
|
if (process.argv.length !== 3) throw new Error('Use: node deletePad.js $PADID');
|
||||||
|
|
||||||
|
@ -26,13 +29,13 @@ const filePath = path.join(__dirname, '../APIKEY.txt');
|
||||||
const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
let apiVersion = await api.get('/api/');
|
let apiVersion = await axios.get('/api/');
|
||||||
apiVersion = apiVersion.body.currentVersion;
|
apiVersion = apiVersion.data.currentVersion;
|
||||||
if (!apiVersion) throw new Error('No version set in API');
|
if (!apiVersion) throw new Error('No version set in API');
|
||||||
|
|
||||||
// Now we know the latest API version, let's delete pad
|
// Now we know the latest API version, let's delete pad
|
||||||
const uri = `/api/${apiVersion}/deletePad?apikey=${apikey}&padID=${padId}`;
|
const uri = `/api/${apiVersion}/deletePad?apikey=${apikey}&padID=${padId}`;
|
||||||
const deleteAttempt = await api.post(uri);
|
const deleteAttempt = await axios.post(uri);
|
||||||
if (deleteAttempt.body.code === 1) throw new Error(`Error deleting pad ${deleteAttempt.body}`);
|
if (deleteAttempt.data.code === 1) throw new Error(`Error deleting pad ${deleteAttempt.data}`);
|
||||||
console.log('Deleted pad', deleteAttempt.body);
|
console.log('Deleted pad', deleteAttempt.data);
|
||||||
})();
|
})();
|
|
@ -1,18 +0,0 @@
|
||||||
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to
|
|
||||||
deal in the Software without restriction, including without limitation the
|
|
||||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
IN THE SOFTWARE.
|
|
|
@ -1,76 +0,0 @@
|
||||||
Here's how the node docs work.
|
|
||||||
|
|
||||||
Each type of heading has a description block.
|
|
||||||
|
|
||||||
|
|
||||||
## module
|
|
||||||
|
|
||||||
Stability: 3 - Stable
|
|
||||||
|
|
||||||
description and examples.
|
|
||||||
|
|
||||||
### module.property
|
|
||||||
|
|
||||||
* Type
|
|
||||||
|
|
||||||
description of the property.
|
|
||||||
|
|
||||||
### module.someFunction(x, y, [z=100])
|
|
||||||
|
|
||||||
* `x` {String} the description of the string
|
|
||||||
* `y` {Boolean} Should I stay or should I go?
|
|
||||||
* `z` {Number} How many zebras to bring.
|
|
||||||
|
|
||||||
A description of the function.
|
|
||||||
|
|
||||||
### Event: 'blerg'
|
|
||||||
|
|
||||||
* Argument: SomeClass object.
|
|
||||||
|
|
||||||
Modules don't usually raise events on themselves. `cluster` is the
|
|
||||||
only exception.
|
|
||||||
|
|
||||||
## Class: SomeClass
|
|
||||||
|
|
||||||
description of the class.
|
|
||||||
|
|
||||||
### Class Method: SomeClass.classMethod(anArg)
|
|
||||||
|
|
||||||
* `anArg` {Object} Just an argument
|
|
||||||
* `field` {String} anArg can have this field.
|
|
||||||
* `field2` {Boolean} Another field. Default: `false`.
|
|
||||||
* Return: {Boolean} `true` if it worked.
|
|
||||||
|
|
||||||
Description of the method for humans.
|
|
||||||
|
|
||||||
### someClass.nextSibling()
|
|
||||||
|
|
||||||
* Return: {SomeClass object | null} The next someClass in line.
|
|
||||||
|
|
||||||
### someClass.someProperty
|
|
||||||
|
|
||||||
* String
|
|
||||||
|
|
||||||
The indication of what someProperty is.
|
|
||||||
|
|
||||||
### Event: 'grelb'
|
|
||||||
|
|
||||||
* `isBlerg` {Boolean}
|
|
||||||
|
|
||||||
This event is emitted on instances of SomeClass, not on the module itself.
|
|
||||||
|
|
||||||
|
|
||||||
* Modules have (description, Properties, Functions, Classes, Examples)
|
|
||||||
* Properties have (type, description)
|
|
||||||
* Functions have (list of arguments, description)
|
|
||||||
* Classes have (description, Properties, Methods, Events)
|
|
||||||
* Events have (list of arguments, description)
|
|
||||||
* Methods have (list of arguments, description)
|
|
||||||
* Properties have (type, description)
|
|
||||||
|
|
||||||
# CLI usage
|
|
||||||
|
|
||||||
Run the following from the etherpad-lite root directory:
|
|
||||||
```sh
|
|
||||||
$ node src/bin/doc/generate doc/index.md --format=html --template=doc/template.html > out.html
|
|
||||||
```
|
|
|
@ -1,122 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// Copyright Joyent, Inc. and other Node contributors.
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
// copy of this software and associated documentation files (the
|
|
||||||
// "Software"), to deal in the Software without restriction, including
|
|
||||||
// without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
||||||
// persons to whom the Software is furnished to do so, subject to the
|
|
||||||
// following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included
|
|
||||||
// in all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
||||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
||||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
||||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
// parse the args.
|
|
||||||
// Don't use nopt or whatever for this. It's simple enough.
|
|
||||||
|
|
||||||
const args = process.argv.slice(2);
|
|
||||||
let format = 'json';
|
|
||||||
let template = null;
|
|
||||||
let inputFile = null;
|
|
||||||
|
|
||||||
args.forEach((arg) => {
|
|
||||||
if (!arg.match(/^--/)) {
|
|
||||||
inputFile = arg;
|
|
||||||
} else if (arg.match(/^--format=/)) {
|
|
||||||
format = arg.replace(/^--format=/, '');
|
|
||||||
} else if (arg.match(/^--template=/)) {
|
|
||||||
template = arg.replace(/^--template=/, '');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
if (!inputFile) {
|
|
||||||
throw new Error('No input file specified');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
console.error('Input file = %s', inputFile);
|
|
||||||
fs.readFile(inputFile, 'utf8', (er, input) => {
|
|
||||||
if (er) throw er;
|
|
||||||
// process the input for @include lines
|
|
||||||
processIncludes(inputFile, input, next);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const includeExpr = /^@include\s+([A-Za-z0-9-_/]+)(?:\.)?([a-zA-Z]*)$/gmi;
|
|
||||||
const includeData = {};
|
|
||||||
const processIncludes = (inputFile, input, cb) => {
|
|
||||||
const includes = input.match(includeExpr);
|
|
||||||
if (includes == null) return cb(null, input);
|
|
||||||
let errState = null;
|
|
||||||
console.error(includes);
|
|
||||||
let incCount = includes.length;
|
|
||||||
if (incCount === 0) cb(null, input);
|
|
||||||
|
|
||||||
includes.forEach((include) => {
|
|
||||||
let fname = include.replace(/^@include\s+/, '');
|
|
||||||
if (!fname.match(/\.md$/)) fname += '.md';
|
|
||||||
|
|
||||||
if (Object.prototype.hasOwnProperty.call(includeData, fname)) {
|
|
||||||
input = input.split(include).join(includeData[fname]);
|
|
||||||
incCount--;
|
|
||||||
if (incCount === 0) {
|
|
||||||
return cb(null, input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fullFname = path.resolve(path.dirname(inputFile), fname);
|
|
||||||
fs.readFile(fullFname, 'utf8', (er, inc) => {
|
|
||||||
if (errState) return;
|
|
||||||
if (er) return cb(errState = er);
|
|
||||||
processIncludes(fullFname, inc, (er, inc) => {
|
|
||||||
if (errState) return;
|
|
||||||
if (er) return cb(errState = er);
|
|
||||||
incCount--;
|
|
||||||
includeData[fname] = inc;
|
|
||||||
input = input.split(include).join(includeData[fname]);
|
|
||||||
if (incCount === 0) {
|
|
||||||
return cb(null, input);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const next = (er, input) => {
|
|
||||||
if (er) throw er;
|
|
||||||
switch (format) {
|
|
||||||
case 'json':
|
|
||||||
require('./json.js')(input, inputFile, (er, obj) => {
|
|
||||||
console.log(JSON.stringify(obj, null, 2));
|
|
||||||
if (er) throw er;
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'html':
|
|
||||||
require('./html.js')(input, inputFile, template, (er, html) => {
|
|
||||||
if (er) throw er;
|
|
||||||
console.log(html);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(`Invalid format: ${format}`);
|
|
||||||
}
|
|
||||||
};
|
|
165
bin/doc/html.js
165
bin/doc/html.js
|
@ -1,165 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// Copyright Joyent, Inc. and other Node contributors.
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
// copy of this software and associated documentation files (the
|
|
||||||
// "Software"), to deal in the Software without restriction, including
|
|
||||||
// without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
||||||
// persons to whom the Software is furnished to do so, subject to the
|
|
||||||
// following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included
|
|
||||||
// in all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
||||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
||||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
||||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const marked = require('marked');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
|
|
||||||
const toHTML = (input, filename, template, cb) => {
|
|
||||||
const lexed = marked.lexer(input);
|
|
||||||
fs.readFile(template, 'utf8', (er, template) => {
|
|
||||||
if (er) return cb(er);
|
|
||||||
render(lexed, filename, template, cb);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
module.exports = toHTML;
|
|
||||||
|
|
||||||
const render = (lexed, filename, template, cb) => {
|
|
||||||
// get the section
|
|
||||||
const section = getSection(lexed);
|
|
||||||
|
|
||||||
filename = path.basename(filename, '.md');
|
|
||||||
|
|
||||||
lexed = parseLists(lexed);
|
|
||||||
|
|
||||||
// generate the table of contents.
|
|
||||||
// this mutates the lexed contents in-place.
|
|
||||||
buildToc(lexed, filename, (er, toc) => {
|
|
||||||
if (er) return cb(er);
|
|
||||||
|
|
||||||
template = template.replace(/__FILENAME__/g, filename);
|
|
||||||
template = template.replace(/__SECTION__/g, section);
|
|
||||||
template = template.replace(/__TOC__/g, toc);
|
|
||||||
|
|
||||||
// content has to be the last thing we do with
|
|
||||||
// the lexed tokens, because it's destructive.
|
|
||||||
const content = marked.parser(lexed);
|
|
||||||
template = template.replace(/__CONTENT__/g, content);
|
|
||||||
|
|
||||||
cb(null, template);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// just update the list item text in-place.
|
|
||||||
// lists that come right after a heading are what we're after.
|
|
||||||
const parseLists = (input) => {
|
|
||||||
let state = null;
|
|
||||||
let depth = 0;
|
|
||||||
const output = [];
|
|
||||||
output.links = input.links;
|
|
||||||
input.forEach((tok) => {
|
|
||||||
if (state == null) {
|
|
||||||
if (tok.type === 'heading') {
|
|
||||||
state = 'AFTERHEADING';
|
|
||||||
}
|
|
||||||
output.push(tok);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state === 'AFTERHEADING') {
|
|
||||||
if (tok.type === 'list_start') {
|
|
||||||
state = 'LIST';
|
|
||||||
if (depth === 0) {
|
|
||||||
output.push({type: 'html', text: '<div class="signature">'});
|
|
||||||
}
|
|
||||||
depth++;
|
|
||||||
output.push(tok);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state = null;
|
|
||||||
output.push(tok);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state === 'LIST') {
|
|
||||||
if (tok.type === 'list_start') {
|
|
||||||
depth++;
|
|
||||||
output.push(tok);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (tok.type === 'list_end') {
|
|
||||||
depth--;
|
|
||||||
if (depth === 0) {
|
|
||||||
state = null;
|
|
||||||
output.push({type: 'html', text: '</div>'});
|
|
||||||
}
|
|
||||||
output.push(tok);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (tok.text) {
|
|
||||||
tok.text = parseListItem(tok.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output.push(tok);
|
|
||||||
});
|
|
||||||
|
|
||||||
return output;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const parseListItem = (text) => {
|
|
||||||
text = text.replace(/\{([^}]+)\}/, '<span class="type">$1</span>');
|
|
||||||
// XXX maybe put more stuff here?
|
|
||||||
return text;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// section is just the first heading
|
|
||||||
const getSection = (lexed) => {
|
|
||||||
for (let i = 0, l = lexed.length; i < l; i++) {
|
|
||||||
const tok = lexed[i];
|
|
||||||
if (tok.type === 'heading') return tok.text;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const buildToc = (lexed, filename, cb) => {
|
|
||||||
let toc = [];
|
|
||||||
let depth = 0;
|
|
||||||
|
|
||||||
marked.setOptions({
|
|
||||||
headerIds: true,
|
|
||||||
headerPrefix: `${filename}_`,
|
|
||||||
});
|
|
||||||
|
|
||||||
lexed.forEach((tok) => {
|
|
||||||
if (tok.type !== 'heading') return;
|
|
||||||
if (tok.depth - depth > 1) {
|
|
||||||
return cb(new Error(`Inappropriate heading level\n${JSON.stringify(tok)}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
depth = tok.depth;
|
|
||||||
|
|
||||||
const slugger = new marked.Slugger();
|
|
||||||
const id = slugger.slug(`${filename}_${tok.text.trim()}`);
|
|
||||||
|
|
||||||
toc.push(`${new Array((depth - 1) * 2 + 1).join(' ')}* <a href="#${id}">${tok.text}</a>`);
|
|
||||||
|
|
||||||
tok.text += `<span><a class="mark" href="#${id}" ` +
|
|
||||||
`id="${id}">#</a></span>`;
|
|
||||||
});
|
|
||||||
|
|
||||||
toc = marked.parse(toc.join('\n'));
|
|
||||||
cb(null, toc);
|
|
||||||
};
|
|
556
bin/doc/json.js
556
bin/doc/json.js
|
@ -1,556 +0,0 @@
|
||||||
'use strict';
|
|
||||||
// Copyright Joyent, Inc. and other Node contributors.
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
// copy of this software and associated documentation files (the
|
|
||||||
// "Software"), to deal in the Software without restriction, including
|
|
||||||
// without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
||||||
// persons to whom the Software is furnished to do so, subject to the
|
|
||||||
// following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included
|
|
||||||
// in all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
||||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
||||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
||||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
module.exports = doJSON;
|
|
||||||
|
|
||||||
// Take the lexed input, and return a JSON-encoded object
|
|
||||||
// A module looks like this: https://gist.github.com/1777387
|
|
||||||
|
|
||||||
const marked = require('marked');
|
|
||||||
|
|
||||||
const doJSON = (input, filename, cb) => {
|
|
||||||
const root = {source: filename};
|
|
||||||
const stack = [root];
|
|
||||||
let depth = 0;
|
|
||||||
let current = root;
|
|
||||||
let state = null;
|
|
||||||
const lexed = marked.lexer(input);
|
|
||||||
lexed.forEach((tok) => {
|
|
||||||
const type = tok.type;
|
|
||||||
let text = tok.text;
|
|
||||||
|
|
||||||
// <!-- type = module -->
|
|
||||||
// This is for cases where the markdown semantic structure is lacking.
|
|
||||||
if (type === 'paragraph' || type === 'html') {
|
|
||||||
const metaExpr = /<!--([^=]+)=([^-]+)-->\n*/g;
|
|
||||||
text = text.replace(metaExpr, (_0, k, v) => {
|
|
||||||
current[k.trim()] = v.trim();
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
text = text.trim();
|
|
||||||
if (!text) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'heading' &&
|
|
||||||
!text.trim().match(/^example/i)) {
|
|
||||||
if (tok.depth - depth > 1) {
|
|
||||||
return cb(new Error(`Inappropriate heading level\n${
|
|
||||||
JSON.stringify(tok)}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sometimes we have two headings with a single
|
|
||||||
// blob of description. Treat as a clone.
|
|
||||||
if (current &&
|
|
||||||
state === 'AFTERHEADING' &&
|
|
||||||
depth === tok.depth) {
|
|
||||||
const clone = current;
|
|
||||||
current = newSection(tok);
|
|
||||||
current.clone = clone;
|
|
||||||
// don't keep it around on the stack.
|
|
||||||
stack.pop();
|
|
||||||
} else {
|
|
||||||
// if the level is greater than the current depth,
|
|
||||||
// then it's a child, so we should just leave the stack
|
|
||||||
// as it is.
|
|
||||||
// However, if it's a sibling or higher, then it implies
|
|
||||||
// the closure of the other sections that came before.
|
|
||||||
// root is always considered the level=0 section,
|
|
||||||
// and the lowest heading is 1, so this should always
|
|
||||||
// result in having a valid parent node.
|
|
||||||
let d = tok.depth;
|
|
||||||
while (d <= depth) {
|
|
||||||
finishSection(stack.pop(), stack[stack.length - 1]);
|
|
||||||
d++;
|
|
||||||
}
|
|
||||||
current = newSection(tok);
|
|
||||||
}
|
|
||||||
|
|
||||||
depth = tok.depth;
|
|
||||||
stack.push(current);
|
|
||||||
state = 'AFTERHEADING';
|
|
||||||
return;
|
|
||||||
} // heading
|
|
||||||
|
|
||||||
// Immediately after a heading, we can expect the following
|
|
||||||
//
|
|
||||||
// { type: 'code', text: 'Stability: ...' },
|
|
||||||
//
|
|
||||||
// a list: starting with list_start, ending with list_end,
|
|
||||||
// maybe containing other nested lists in each item.
|
|
||||||
//
|
|
||||||
// If one of these isn't found, then anything that comes between
|
|
||||||
// here and the next heading should be parsed as the desc.
|
|
||||||
let stability;
|
|
||||||
if (state === 'AFTERHEADING') {
|
|
||||||
if (type === 'code' &&
|
|
||||||
(stability = text.match(/^Stability: ([0-5])(?:\s*-\s*)?(.*)$/))) {
|
|
||||||
current.stability = parseInt(stability[1], 10);
|
|
||||||
current.stabilityText = stability[2].trim();
|
|
||||||
return;
|
|
||||||
} else if (type === 'list_start' && !tok.ordered) {
|
|
||||||
state = 'AFTERHEADING_LIST';
|
|
||||||
current.list = current.list || [];
|
|
||||||
current.list.push(tok);
|
|
||||||
current.list.level = 1;
|
|
||||||
} else {
|
|
||||||
current.desc = current.desc || [];
|
|
||||||
if (!Array.isArray(current.desc)) {
|
|
||||||
current.shortDesc = current.desc;
|
|
||||||
current.desc = [];
|
|
||||||
}
|
|
||||||
current.desc.push(tok);
|
|
||||||
state = 'DESC';
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state === 'AFTERHEADING_LIST') {
|
|
||||||
current.list.push(tok);
|
|
||||||
if (type === 'list_start') {
|
|
||||||
current.list.level++;
|
|
||||||
} else if (type === 'list_end') {
|
|
||||||
current.list.level--;
|
|
||||||
}
|
|
||||||
if (current.list.level === 0) {
|
|
||||||
state = 'AFTERHEADING';
|
|
||||||
processList(current);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
current.desc = current.desc || [];
|
|
||||||
current.desc.push(tok);
|
|
||||||
});
|
|
||||||
|
|
||||||
// finish any sections left open
|
|
||||||
while (root !== (current = stack.pop())) {
|
|
||||||
finishSection(current, stack[stack.length - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cb(null, root);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// go from something like this:
|
|
||||||
// [ { type: 'list_item_start' },
|
|
||||||
// { type: 'text',
|
|
||||||
// text: '`settings` Object, Optional' },
|
|
||||||
// { type: 'list_start', ordered: false },
|
|
||||||
// { type: 'list_item_start' },
|
|
||||||
// { type: 'text',
|
|
||||||
// text: 'exec: String, file path to worker file. Default: `__filename`' },
|
|
||||||
// { type: 'list_item_end' },
|
|
||||||
// { type: 'list_item_start' },
|
|
||||||
// { type: 'text',
|
|
||||||
// text: 'args: Array, string arguments passed to worker.' },
|
|
||||||
// { type: 'text',
|
|
||||||
// text: 'Default: `process.argv.slice(2)`' },
|
|
||||||
// { type: 'list_item_end' },
|
|
||||||
// { type: 'list_item_start' },
|
|
||||||
// { type: 'text',
|
|
||||||
// text: 'silent: Boolean, whether or not to send output to parent\'s stdio.' },
|
|
||||||
// { type: 'text', text: 'Default: `false`' },
|
|
||||||
// { type: 'space' },
|
|
||||||
// { type: 'list_item_end' },
|
|
||||||
// { type: 'list_end' },
|
|
||||||
// { type: 'list_item_end' },
|
|
||||||
// { type: 'list_end' } ]
|
|
||||||
// to something like:
|
|
||||||
// [ { name: 'settings',
|
|
||||||
// type: 'object',
|
|
||||||
// optional: true,
|
|
||||||
// settings:
|
|
||||||
// [ { name: 'exec',
|
|
||||||
// type: 'string',
|
|
||||||
// desc: 'file path to worker file',
|
|
||||||
// default: '__filename' },
|
|
||||||
// { name: 'args',
|
|
||||||
// type: 'array',
|
|
||||||
// default: 'process.argv.slice(2)',
|
|
||||||
// desc: 'string arguments passed to worker.' },
|
|
||||||
// { name: 'silent',
|
|
||||||
// type: 'boolean',
|
|
||||||
// desc: 'whether or not to send output to parent\'s stdio.',
|
|
||||||
// default: 'false' } ] } ]
|
|
||||||
|
|
||||||
const processList = (section) => {
|
|
||||||
const list = section.list;
|
|
||||||
const values = [];
|
|
||||||
let current;
|
|
||||||
const stack = [];
|
|
||||||
|
|
||||||
// for now, *just* build the hierarchical list
|
|
||||||
list.forEach((tok) => {
|
|
||||||
const type = tok.type;
|
|
||||||
if (type === 'space') return;
|
|
||||||
if (type === 'list_item_start') {
|
|
||||||
if (!current) {
|
|
||||||
const n = {};
|
|
||||||
values.push(n);
|
|
||||||
current = n;
|
|
||||||
} else {
|
|
||||||
current.options = current.options || [];
|
|
||||||
stack.push(current);
|
|
||||||
const n = {};
|
|
||||||
current.options.push(n);
|
|
||||||
current = n;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} else if (type === 'list_item_end') {
|
|
||||||
if (!current) {
|
|
||||||
throw new Error(`invalid list - end without current item\n${
|
|
||||||
JSON.stringify(tok)}\n${
|
|
||||||
JSON.stringify(list)}`);
|
|
||||||
}
|
|
||||||
current = stack.pop();
|
|
||||||
} else if (type === 'text') {
|
|
||||||
if (!current) {
|
|
||||||
throw new Error(`invalid list - text without current item\n${
|
|
||||||
JSON.stringify(tok)}\n${
|
|
||||||
JSON.stringify(list)}`);
|
|
||||||
}
|
|
||||||
current.textRaw = current.textRaw || '';
|
|
||||||
current.textRaw += `${tok.text} `;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// shove the name in there for properties, since they are always
|
|
||||||
// just going to be the value etc.
|
|
||||||
if (section.type === 'property' && values[0]) {
|
|
||||||
values[0].textRaw = `\`${section.name}\` ${values[0].textRaw}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// now pull the actual values out of the text bits.
|
|
||||||
values.forEach(parseListItem);
|
|
||||||
|
|
||||||
// Now figure out what this list actually means.
|
|
||||||
// depending on the section type, the list could be different things.
|
|
||||||
|
|
||||||
switch (section.type) {
|
|
||||||
case 'ctor':
|
|
||||||
case 'classMethod':
|
|
||||||
case 'method': {
|
|
||||||
// each item is an argument, unless the name is 'return',
|
|
||||||
// in which case it's the return value.
|
|
||||||
section.signatures = section.signatures || [];
|
|
||||||
const sig = {};
|
|
||||||
section.signatures.push(sig);
|
|
||||||
sig.params = values.filter((v) => {
|
|
||||||
if (v.name === 'return') {
|
|
||||||
sig.return = v;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
parseSignature(section.textRaw, sig);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'property': {
|
|
||||||
// there should be only one item, which is the value.
|
|
||||||
// copy the data up to the section.
|
|
||||||
const value = values[0] || {};
|
|
||||||
delete value.name;
|
|
||||||
section.typeof = value.type;
|
|
||||||
delete value.type;
|
|
||||||
Object.keys(value).forEach((k) => {
|
|
||||||
section[k] = value[k];
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'event': {
|
|
||||||
// event: each item is an argument.
|
|
||||||
section.params = values;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete section.list;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// textRaw = "someobject.someMethod(a, [b=100], [c])"
|
|
||||||
const parseSignature = (text, sig) => {
|
|
||||||
let params = text.match(paramExpr);
|
|
||||||
if (!params) return;
|
|
||||||
params = params[1];
|
|
||||||
// the ] is irrelevant. [ indicates optionalness.
|
|
||||||
params = params.replace(/\]/g, '');
|
|
||||||
params = params.split(/,/);
|
|
||||||
params.forEach((p, i, _) => {
|
|
||||||
p = p.trim();
|
|
||||||
if (!p) return;
|
|
||||||
let param = sig.params[i];
|
|
||||||
let optional = false;
|
|
||||||
let def;
|
|
||||||
// [foo] -> optional
|
|
||||||
if (p.charAt(0) === '[') {
|
|
||||||
optional = true;
|
|
||||||
p = p.substr(1);
|
|
||||||
}
|
|
||||||
const eq = p.indexOf('=');
|
|
||||||
if (eq !== -1) {
|
|
||||||
def = p.substr(eq + 1);
|
|
||||||
p = p.substr(0, eq);
|
|
||||||
}
|
|
||||||
if (!param) {
|
|
||||||
param = sig.params[i] = {name: p};
|
|
||||||
}
|
|
||||||
// at this point, the name should match.
|
|
||||||
if (p !== param.name) {
|
|
||||||
console.error('Warning: invalid param "%s"', p);
|
|
||||||
console.error(` > ${JSON.stringify(param)}`);
|
|
||||||
console.error(` > ${text}`);
|
|
||||||
}
|
|
||||||
if (optional) param.optional = true;
|
|
||||||
if (def !== undefined) param.default = def;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const parseListItem = (item) => {
|
|
||||||
if (item.options) item.options.forEach(parseListItem);
|
|
||||||
if (!item.textRaw) return;
|
|
||||||
|
|
||||||
// the goal here is to find the name, type, default, and optional.
|
|
||||||
// anything left over is 'desc'
|
|
||||||
let text = item.textRaw.trim();
|
|
||||||
// text = text.replace(/^(Argument|Param)s?\s*:?\s*/i, '');
|
|
||||||
|
|
||||||
text = text.replace(/^, /, '').trim();
|
|
||||||
const retExpr = /^returns?\s*:?\s*/i;
|
|
||||||
const ret = text.match(retExpr);
|
|
||||||
if (ret) {
|
|
||||||
item.name = 'return';
|
|
||||||
text = text.replace(retExpr, '');
|
|
||||||
} else {
|
|
||||||
const nameExpr = /^['`"]?([^'`": {]+)['`"]?\s*:?\s*/;
|
|
||||||
const name = text.match(nameExpr);
|
|
||||||
if (name) {
|
|
||||||
item.name = name[1];
|
|
||||||
text = text.replace(nameExpr, '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
text = text.trim();
|
|
||||||
const defaultExpr = /\(default\s*[:=]?\s*['"`]?([^, '"`]*)['"`]?\)/i;
|
|
||||||
const def = text.match(defaultExpr);
|
|
||||||
if (def) {
|
|
||||||
item.default = def[1];
|
|
||||||
text = text.replace(defaultExpr, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
text = text.trim();
|
|
||||||
const typeExpr = /^\{([^}]+)\}/;
|
|
||||||
const type = text.match(typeExpr);
|
|
||||||
if (type) {
|
|
||||||
item.type = type[1];
|
|
||||||
text = text.replace(typeExpr, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
text = text.trim();
|
|
||||||
const optExpr = /^Optional\.|(?:, )?Optional$/;
|
|
||||||
const optional = text.match(optExpr);
|
|
||||||
if (optional) {
|
|
||||||
item.optional = true;
|
|
||||||
text = text.replace(optExpr, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
text = text.replace(/^\s*-\s*/, '');
|
|
||||||
text = text.trim();
|
|
||||||
if (text) item.desc = text;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const finishSection = (section, parent) => {
|
|
||||||
if (!section || !parent) {
|
|
||||||
throw new Error(`Invalid finishSection call\n${
|
|
||||||
JSON.stringify(section)}\n${
|
|
||||||
JSON.stringify(parent)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!section.type) {
|
|
||||||
section.type = 'module';
|
|
||||||
if (parent && (parent.type === 'misc')) {
|
|
||||||
section.type = 'misc';
|
|
||||||
}
|
|
||||||
section.displayName = section.name;
|
|
||||||
section.name = section.name.toLowerCase()
|
|
||||||
.trim().replace(/\s+/g, '_');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (section.desc && Array.isArray(section.desc)) {
|
|
||||||
section.desc.links = section.desc.links || [];
|
|
||||||
section.desc = marked.parser(section.desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!section.list) section.list = [];
|
|
||||||
processList(section);
|
|
||||||
|
|
||||||
// classes sometimes have various 'ctor' children
|
|
||||||
// which are actually just descriptions of a constructor
|
|
||||||
// class signature.
|
|
||||||
// Merge them into the parent.
|
|
||||||
if (section.type === 'class' && section.ctors) {
|
|
||||||
section.signatures = section.signatures || [];
|
|
||||||
const sigs = section.signatures;
|
|
||||||
section.ctors.forEach((ctor) => {
|
|
||||||
ctor.signatures = ctor.signatures || [{}];
|
|
||||||
ctor.signatures.forEach((sig) => {
|
|
||||||
sig.desc = ctor.desc;
|
|
||||||
});
|
|
||||||
sigs.push(...ctor.signatures);
|
|
||||||
});
|
|
||||||
delete section.ctors;
|
|
||||||
}
|
|
||||||
|
|
||||||
// properties are a bit special.
|
|
||||||
// their "type" is the type of object, not "property"
|
|
||||||
if (section.properties) {
|
|
||||||
section.properties.forEach((p) => {
|
|
||||||
if (p.typeof) p.type = p.typeof;
|
|
||||||
else delete p.type;
|
|
||||||
delete p.typeof;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle clones
|
|
||||||
if (section.clone) {
|
|
||||||
const clone = section.clone;
|
|
||||||
delete section.clone;
|
|
||||||
delete clone.clone;
|
|
||||||
deepCopy(section, clone);
|
|
||||||
finishSection(clone, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
let plur;
|
|
||||||
if (section.type.slice(-1) === 's') {
|
|
||||||
plur = `${section.type}es`;
|
|
||||||
} else if (section.type.slice(-1) === 'y') {
|
|
||||||
plur = section.type.replace(/y$/, 'ies');
|
|
||||||
} else {
|
|
||||||
plur = `${section.type}s`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the parent's type is 'misc', then it's just a random
|
|
||||||
// collection of stuff, like the "globals" section.
|
|
||||||
// Make the children top-level items.
|
|
||||||
if (section.type === 'misc') {
|
|
||||||
Object.keys(section).forEach((k) => {
|
|
||||||
switch (k) {
|
|
||||||
case 'textRaw':
|
|
||||||
case 'name':
|
|
||||||
case 'type':
|
|
||||||
case 'desc':
|
|
||||||
case 'miscs':
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
if (parent.type === 'misc') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Array.isArray(k) && parent[k]) {
|
|
||||||
parent[k] = parent[k].concat(section[k]);
|
|
||||||
} else if (!parent[k]) {
|
|
||||||
parent[k] = section[k];
|
|
||||||
} else {
|
|
||||||
// parent already has, and it's not an array.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
parent[plur] = parent[plur] || [];
|
|
||||||
parent[plur].push(section);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Not a general purpose deep copy.
|
|
||||||
// But sufficient for these basic things.
|
|
||||||
const deepCopy = (src, dest) => {
|
|
||||||
Object.keys(src).filter((k) => !Object.prototype.hasOwnProperty.call(dest, k)).forEach((k) => {
|
|
||||||
dest[k] = deepCopy_(src[k]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const deepCopy_ = (src) => {
|
|
||||||
if (!src) return src;
|
|
||||||
if (Array.isArray(src)) {
|
|
||||||
const c = new Array(src.length);
|
|
||||||
src.forEach((v, i) => {
|
|
||||||
c[i] = deepCopy_(v);
|
|
||||||
});
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
if (typeof src === 'object') {
|
|
||||||
const c = {};
|
|
||||||
Object.keys(src).forEach((k) => {
|
|
||||||
c[k] = deepCopy_(src[k]);
|
|
||||||
});
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
return src;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// these parse out the contents of an H# tag
|
|
||||||
const eventExpr = /^Event(?::|\s)+['"]?([^"']+).*$/i;
|
|
||||||
const classExpr = /^Class:\s*([^ ]+).*?$/i;
|
|
||||||
const propExpr = /^(?:property:?\s*)?[^.]+\.([^ .()]+)\s*?$/i;
|
|
||||||
const braceExpr = /^(?:property:?\s*)?[^.[]+(\[[^\]]+\])\s*?$/i;
|
|
||||||
const classMethExpr =
|
|
||||||
/^class\s*method\s*:?[^.]+\.([^ .()]+)\([^)]*\)\s*?$/i;
|
|
||||||
const methExpr =
|
|
||||||
/^(?:method:?\s*)?(?:[^.]+\.)?([^ .()]+)\([^)]*\)\s*?$/i;
|
|
||||||
const newExpr = /^new ([A-Z][a-z]+)\([^)]*\)\s*?$/;
|
|
||||||
const paramExpr = /\((.*)\);?$/;
|
|
||||||
|
|
||||||
const newSection = (tok) => {
|
|
||||||
const section = {};
|
|
||||||
// infer the type from the text.
|
|
||||||
const text = section.textRaw = tok.text;
|
|
||||||
if (text.match(eventExpr)) {
|
|
||||||
section.type = 'event';
|
|
||||||
section.name = text.replace(eventExpr, '$1');
|
|
||||||
} else if (text.match(classExpr)) {
|
|
||||||
section.type = 'class';
|
|
||||||
section.name = text.replace(classExpr, '$1');
|
|
||||||
} else if (text.match(braceExpr)) {
|
|
||||||
section.type = 'property';
|
|
||||||
section.name = text.replace(braceExpr, '$1');
|
|
||||||
} else if (text.match(propExpr)) {
|
|
||||||
section.type = 'property';
|
|
||||||
section.name = text.replace(propExpr, '$1');
|
|
||||||
} else if (text.match(classMethExpr)) {
|
|
||||||
section.type = 'classMethod';
|
|
||||||
section.name = text.replace(classMethExpr, '$1');
|
|
||||||
} else if (text.match(methExpr)) {
|
|
||||||
section.type = 'method';
|
|
||||||
section.name = text.replace(methExpr, '$1');
|
|
||||||
} else if (text.match(newExpr)) {
|
|
||||||
section.type = 'ctor';
|
|
||||||
section.name = text.replace(newExpr, '$1');
|
|
||||||
} else {
|
|
||||||
section.name = text;
|
|
||||||
}
|
|
||||||
return section;
|
|
||||||
};
|
|
|
@ -1,26 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
// Checks the health of Etherpad by visiting http://localhost:9001/health. Returns 0 on success, 1
|
|
||||||
// on error as required by the Dockerfile HEALTHCHECK instruction.
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
|
||||||
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
|
||||||
|
|
||||||
const assert = require('assert').strict;
|
|
||||||
const superagent = require('superagent');
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const res = await superagent.get('http://localhost:9001/health')
|
|
||||||
.accept('application/health+json')
|
|
||||||
.buffer(true)
|
|
||||||
.parse(superagent.parse['application/json']);
|
|
||||||
assert(res.ok, `Unexpected HTTP status: ${res.status}`);
|
|
||||||
assert.equal(res.type, 'application/health+json');
|
|
||||||
const {body: {status} = {}} = res;
|
|
||||||
assert(status != null);
|
|
||||||
assert.equal(typeof status, 'string');
|
|
||||||
assert(['pass', 'ok', 'up'].includes(status.toLowerCase()), `Unexpected status: ${status}`);
|
|
||||||
})();
|
|
|
@ -8,10 +8,10 @@
|
||||||
|
|
||||||
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
||||||
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
||||||
|
import util from "util";
|
||||||
|
import process from "process";
|
||||||
|
import dirtyDB from "ueberdb2";
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
process.on('unhandledRejection', (err) => { throw err; });
|
||||||
|
|
||||||
const util = require('util');
|
|
||||||
|
|
||||||
if (process.argv.length !== 3) throw new Error('Use: node extractPadData.js $PADID');
|
if (process.argv.length !== 3) throw new Error('Use: node extractPadData.js $PADID');
|
||||||
|
|
||||||
// get the padID
|
// get the padID
|
||||||
|
@ -19,13 +19,13 @@ const padId = process.argv[2];
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
// initialize database
|
// initialize database
|
||||||
require('./src/node/utils/Settings');
|
require('ep_etherpad-lite/node/utils/Settings');
|
||||||
const db = require('./src/node/db/DB');
|
const db = require('ep_etherpad-lite/node/db/DB');
|
||||||
await db.init();
|
await db.init();
|
||||||
|
|
||||||
// load extra modules
|
// load extra modules
|
||||||
const dirtyDB = require('dirty');
|
const dirtyDB = require('dirty');
|
||||||
const padManager = require('./src/node/db/PadManager');
|
const padManager = require('ep_etherpad-lite/node/db/PadManager');
|
||||||
|
|
||||||
// initialize output database
|
// initialize output database
|
||||||
const dirty = dirtyDB(`${padId}.db`);
|
const dirty = dirtyDB(`${padId}.db`);
|
|
@ -2,20 +2,24 @@
|
||||||
|
|
||||||
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
||||||
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
||||||
|
import util from "util";
|
||||||
|
const fs = require('fs');
|
||||||
|
import log4js from 'log4js';
|
||||||
|
import readline from 'readline';
|
||||||
|
import ueberDB from "ueberdb2";
|
||||||
|
|
||||||
|
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
process.on('unhandledRejection', (err) => { throw err; });
|
||||||
|
|
||||||
const util = require('util');
|
|
||||||
|
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
|
|
||||||
const log = (str) => {
|
const log = (str:string) => {
|
||||||
console.log(`${(Date.now() - startTime) / 1000}\t${str}`);
|
console.log(`${(Date.now() - startTime) / 1000}\t${str}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const unescape = (val) => {
|
const unescape = (val: string) => {
|
||||||
// value is a string
|
// value is a string
|
||||||
if (val.substr(0, 1) === "'") {
|
if (val.substring(0, 1) === "'") {
|
||||||
val = val.substr(0, val.length - 1).substr(1);
|
val = val.substring(0, val.length - 1).substring(1);
|
||||||
|
|
||||||
return val.replace(/\\[0nrbtZ\\'"]/g, (s) => {
|
return val.replace(/\\[0nrbtZ\\'"]/g, (s) => {
|
||||||
switch (s) {
|
switch (s) {
|
||||||
|
@ -25,7 +29,7 @@ const unescape = (val) => {
|
||||||
case '\\b': return '\b';
|
case '\\b': return '\b';
|
||||||
case '\\t': return '\t';
|
case '\\t': return '\t';
|
||||||
case '\\Z': return '\x1a';
|
case '\\Z': return '\x1a';
|
||||||
default: return s.substr(1);
|
default: return s.substring(1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -46,18 +50,13 @@ const unescape = (val) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const fs = require('fs');
|
|
||||||
const log4js = require('log4js');
|
|
||||||
const readline = require('readline');
|
|
||||||
const settings = require('./src/node/utils/Settings');
|
|
||||||
const ueberDB = require('ueberdb2');
|
|
||||||
|
|
||||||
const dbWrapperSettings = {
|
const dbWrapperSettings = {
|
||||||
cache: 0,
|
cache: 0,
|
||||||
writeInterval: 100,
|
writeInterval: 100,
|
||||||
json: false, // data is already json encoded
|
json: false, // data is already json encoded
|
||||||
};
|
};
|
||||||
const db = new ueberDB.database( // eslint-disable-line new-cap
|
const db = new ueberDB.Database( // eslint-disable-line new-cap
|
||||||
settings.dbType,
|
settings.dbType,
|
||||||
settings.dbSettings,
|
settings.dbSettings,
|
||||||
dbWrapperSettings,
|
dbWrapperSettings,
|
||||||
|
@ -69,7 +68,8 @@ const unescape = (val) => {
|
||||||
if (!sqlFile) throw new Error('Use: node importSqlFile.js $SQLFILE');
|
if (!sqlFile) throw new Error('Use: node importSqlFile.js $SQLFILE');
|
||||||
|
|
||||||
log('initializing db');
|
log('initializing db');
|
||||||
await util.promisify(db.init.bind(db))();
|
const initDb = await util.promisify(db.init.bind(db));
|
||||||
|
await initDb(null);
|
||||||
log('done');
|
log('done');
|
||||||
|
|
||||||
log(`Opening ${sqlFile}...`);
|
log(`Opening ${sqlFile}...`);
|
||||||
|
@ -78,13 +78,14 @@ const unescape = (val) => {
|
||||||
log(`Reading ${sqlFile}...`);
|
log(`Reading ${sqlFile}...`);
|
||||||
let keyNo = 0;
|
let keyNo = 0;
|
||||||
for await (const l of readline.createInterface({input: stream, crlfDelay: Infinity})) {
|
for await (const l of readline.createInterface({input: stream, crlfDelay: Infinity})) {
|
||||||
if (l.substr(0, 27) === 'REPLACE INTO store VALUES (') {
|
if (l.substring(0, 27) === 'REPLACE INTO store VALUES (') {
|
||||||
const pos = l.indexOf("', '");
|
const pos = l.indexOf("', '");
|
||||||
const key = l.substr(28, pos - 28);
|
const key = l.substring(28, pos - 28);
|
||||||
let value = l.substr(pos + 3);
|
let value = l.substring(pos + 3);
|
||||||
value = value.substr(0, value.length - 2);
|
value = value.substring(0, value.length - 2);
|
||||||
console.log(`key: ${key} val: ${value}`);
|
console.log(`key: ${key} val: ${value}`);
|
||||||
console.log(`unval: ${unescape(value)}`);
|
console.log(`unval: ${unescape(value)}`);
|
||||||
|
// @ts-ignore
|
||||||
db.set(key, unescape(value), null);
|
db.set(key, unescape(value), null);
|
||||||
keyNo++;
|
keyNo++;
|
||||||
if (keyNo % 1000 === 0) log(` ${keyNo}`);
|
if (keyNo % 1000 === 0) log(` ${keyNo}`);
|
||||||
|
@ -94,6 +95,7 @@ const unescape = (val) => {
|
||||||
process.stdout.write('done. waiting for db to finish transaction. ' +
|
process.stdout.write('done. waiting for db to finish transaction. ' +
|
||||||
'depended on dbms this may take some time..\n');
|
'depended on dbms this may take some time..\n');
|
||||||
|
|
||||||
await util.promisify(db.close.bind(db))();
|
const closeDB = util.promisify(db.close.bind(db));
|
||||||
|
await closeDB(null);
|
||||||
log(`finished, imported ${keyNo} keys.`);
|
log(`finished, imported ${keyNo} keys.`);
|
||||||
})();
|
})();
|
|
@ -1,5 +1,11 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import process from 'node:process';
|
||||||
|
import ueberDB from "ueberdb2";
|
||||||
|
import log4js from 'log4js';
|
||||||
|
import util from 'util';
|
||||||
|
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
||||||
|
|
||||||
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
||||||
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
process.on('unhandledRejection', (err) => { throw err; });
|
||||||
|
@ -12,17 +18,12 @@ process.on('unhandledRejection', (err) => { throw err; });
|
||||||
// It might be necessary to run the script using more memory:
|
// It might be necessary to run the script using more memory:
|
||||||
// `node --max-old-space-size=4096 src/bin/migrateDirtyDBtoRealDB.js`
|
// `node --max-old-space-size=4096 src/bin/migrateDirtyDBtoRealDB.js`
|
||||||
|
|
||||||
const dirtyDb = require('dirty');
|
|
||||||
const log4js = require('log4js');
|
|
||||||
const settings = require('./src/node/utils/Settings');
|
|
||||||
const ueberDB = require('ueberdb2');
|
|
||||||
const util = require('util');
|
|
||||||
|
|
||||||
const dbWrapperSettings = {
|
const dbWrapperSettings = {
|
||||||
cache: '0', // The cache slows things down when you're mostly writing.
|
cache: '0', // The cache slows things down when you're mostly writing.
|
||||||
writeInterval: 0, // Write directly to the database, don't buffer
|
writeInterval: 0, // Write directly to the database, don't buffer
|
||||||
};
|
};
|
||||||
const db = new ueberDB.database( // eslint-disable-line new-cap
|
const db = new ueberDB.Database( // eslint-disable-line new-cap
|
||||||
settings.dbType,
|
settings.dbType,
|
||||||
settings.dbSettings,
|
settings.dbSettings,
|
||||||
dbWrapperSettings,
|
dbWrapperSettings,
|
||||||
|
@ -30,27 +31,30 @@ process.on('unhandledRejection', (err) => { throw err; });
|
||||||
await db.init();
|
await db.init();
|
||||||
|
|
||||||
console.log('Waiting for dirtyDB to parse its file.');
|
console.log('Waiting for dirtyDB to parse its file.');
|
||||||
const dirty = dirtyDb(`${__dirname}/../var/dirty.db`);
|
const dirty = await new ueberDB.Database('dirty',`${__dirname}/../var/dirty.db`);
|
||||||
const length = await new Promise((resolve) => { dirty.once('load', resolve); });
|
await dirty.init();
|
||||||
|
const keys = await dirty.findKeys('*', '')
|
||||||
|
|
||||||
console.log(`Found ${length} records, processing now.`);
|
console.log(`Found ${keys.length} records, processing now.`);
|
||||||
const p = [];
|
const p: Promise<void>[] = [];
|
||||||
let numWritten = 0;
|
let numWritten = 0;
|
||||||
dirty.forEach((key, value) => {
|
for (const key of keys) {
|
||||||
|
let value = await dirty.get(key);
|
||||||
let bcb, wcb;
|
let bcb, wcb;
|
||||||
p.push(new Promise((resolve, reject) => {
|
p.push(new Promise((resolve, reject) => {
|
||||||
bcb = (err) => { if (err != null) return reject(err); };
|
bcb = (err:any) => { if (err != null) return reject(err); };
|
||||||
wcb = (err) => {
|
wcb = (err:any) => {
|
||||||
if (err != null) return reject(err);
|
if (err != null) return reject(err);
|
||||||
if (++numWritten % 100 === 0) console.log(`Wrote record ${numWritten} of ${length}`);
|
if (++numWritten % 100 === 0) console.log(`Wrote record ${numWritten} of ${length}`);
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
db.set(key, value, bcb, wcb);
|
db.set(key, value, bcb, wcb);
|
||||||
});
|
}
|
||||||
await Promise.all(p);
|
await Promise.all(p);
|
||||||
console.log(`Wrote all ${numWritten} records`);
|
console.log(`Wrote all ${numWritten} records`);
|
||||||
|
|
||||||
await util.promisify(db.close.bind(db))();
|
await db.close(null);
|
||||||
|
await dirty.close(null);
|
||||||
console.log('Finished.');
|
console.log('Finished.');
|
||||||
})();
|
})();
|
37
bin/package.json
Normal file
37
bin/package.json
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"name": "bin",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "checkAllPads.js",
|
||||||
|
"directories": {
|
||||||
|
"doc": "doc"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.6.7",
|
||||||
|
"ep_etherpad-lite": "workspace:../src",
|
||||||
|
"log4js": "^6.9.1",
|
||||||
|
"semver": "^7.6.0",
|
||||||
|
"tsx": "^4.7.1",
|
||||||
|
"ueberdb2": "^4.2.63"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.11.27",
|
||||||
|
"@types/semver": "^7.5.8",
|
||||||
|
"typescript": "^5.4.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"checkPad": "node --import tsx checkPad.ts",
|
||||||
|
"checkAllPads": "node --import tsx checkAllPads.ts",
|
||||||
|
"createUserSession": "node --import tsx createUserSession.ts",
|
||||||
|
"deletePad": "node --import tsx deletePad.ts",
|
||||||
|
"repairPad": "node --import tsx repairPad.ts",
|
||||||
|
"deleteAllGroupSessions": "node --import tsx deleteAllGroupSessions.ts",
|
||||||
|
"importSqlFile": "node --import tsx importSqlFile.ts",
|
||||||
|
"migrateDirtyDBtoRealDB": "node --import tsx migrateDirtyDBtoRealDB.ts",
|
||||||
|
"rebuildPad": "node --import tsx rebuildPad.ts",
|
||||||
|
"stalePlugins": "node --import tsx ./plugins/stalePlugins.ts",
|
||||||
|
"checkPlugins": "node --import tsx ./plugins/checkPlugins.ts"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
34
bin/plugins/checkPlugin.js → bin/plugins/checkPlugin.ts
Executable file → Normal file
34
bin/plugins/checkPlugin.js → bin/plugins/checkPlugin.ts
Executable file → Normal file
|
@ -10,18 +10,18 @@
|
||||||
* node bin/plugins/checkPlugin.js ep_whatever autopush
|
* node bin/plugins/checkPlugin.js ep_whatever autopush
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const process = require('process');
|
import process from 'process';
|
||||||
|
|
||||||
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
||||||
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
process.on('unhandledRejection', (err) => { throw err; });
|
||||||
|
|
||||||
const assert = require('assert').strict;
|
import {strict as assert} from 'assert';
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const fsp = fs.promises;
|
const fsp = fs.promises;
|
||||||
const childProcess = require('child_process');
|
import childProcess from 'child_process';
|
||||||
const log4js = require('log4js');
|
import log4js from 'log4js';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
|
|
||||||
const logger = log4js.getLogger('checkPlugin');
|
const logger = log4js.getLogger('checkPlugin');
|
||||||
|
|
||||||
|
@ -44,23 +44,23 @@ const logger = log4js.getLogger('checkPlugin');
|
||||||
const autoCommit = autoPush || optArgs.includes('autocommit');
|
const autoCommit = autoPush || optArgs.includes('autocommit');
|
||||||
const autoFix = autoCommit || optArgs.includes('autofix');
|
const autoFix = autoCommit || optArgs.includes('autofix');
|
||||||
|
|
||||||
const execSync = (cmd, opts = {}) => (childProcess.execSync(cmd, {
|
const execSync = (cmd:string, opts = {}) => (childProcess.execSync(cmd, {
|
||||||
cwd: `${pluginPath}/`,
|
cwd: `${pluginPath}/`,
|
||||||
...opts,
|
...opts,
|
||||||
}) || '').toString().replace(/\n+$/, '');
|
}) || '').toString().replace(/\n+$/, '');
|
||||||
|
|
||||||
const writePackageJson = async (obj) => {
|
const writePackageJson = async (obj: object) => {
|
||||||
let s = JSON.stringify(obj, null, 2);
|
let s = JSON.stringify(obj, null, 2);
|
||||||
if (s.length && s.slice(s.length - 1) !== '\n') s += '\n';
|
if (s.length && s.slice(s.length - 1) !== '\n') s += '\n';
|
||||||
return await fsp.writeFile(`${pluginPath}/package.json`, s);
|
return await fsp.writeFile(`${pluginPath}/package.json`, s);
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkEntries = (got, want) => {
|
const checkEntries = (got: any, want:any) => {
|
||||||
let changed = false;
|
let changed = false;
|
||||||
for (const [key, val] of Object.entries(want)) {
|
for (const [key, val] of Object.entries(want)) {
|
||||||
try {
|
try {
|
||||||
assert.deepEqual(got[key], val);
|
assert.deepEqual(got[key], val);
|
||||||
} catch (err) {
|
} catch (err:any) {
|
||||||
logger.warn(`${key} possibly outdated.`);
|
logger.warn(`${key} possibly outdated.`);
|
||||||
logger.warn(err.message);
|
logger.warn(err.message);
|
||||||
if (autoFix) {
|
if (autoFix) {
|
||||||
|
@ -72,7 +72,9 @@ const logger = log4js.getLogger('checkPlugin');
|
||||||
return changed;
|
return changed;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateDeps = async (parsedPackageJson, key, wantDeps) => {
|
const updateDeps = async (parsedPackageJson: any, key: string, wantDeps: {
|
||||||
|
[key: string]: string | {ver?: string, overwrite?: boolean}|null
|
||||||
|
}) => {
|
||||||
const {[key]: deps = {}} = parsedPackageJson;
|
const {[key]: deps = {}} = parsedPackageJson;
|
||||||
let changed = false;
|
let changed = false;
|
||||||
for (const [pkg, verInfo] of Object.entries(wantDeps)) {
|
for (const [pkg, verInfo] of Object.entries(wantDeps)) {
|
||||||
|
@ -115,14 +117,14 @@ const logger = log4js.getLogger('checkPlugin');
|
||||||
execSync('git config --get user.email');
|
execSync('git config --get user.email');
|
||||||
}
|
}
|
||||||
if (autoPush) {
|
if (autoPush) {
|
||||||
if (!['master', 'main'].includes(br)) throw new Error('master/main not checked out');
|
if (!['master', 'main'].includes(br!)) throw new Error('master/main not checked out');
|
||||||
execSync('git rev-parse --verify @{u}');
|
execSync('git rev-parse --verify @{u}');
|
||||||
execSync('git pull --ff-only', {stdio: 'inherit'});
|
execSync('git pull --ff-only', {stdio: 'inherit'});
|
||||||
if (execSync('git rev-list @{u}...') !== '') throw new Error('repo contains unpushed commits');
|
if (execSync('git rev-list @{u}...') !== '') throw new Error('repo contains unpushed commits');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkFile = async (srcFn, dstFn, overwrite = true) => {
|
const checkFile = async (srcFn: string, dstFn:string, overwrite = true) => {
|
||||||
const outFn = path.join(pluginPath, dstFn);
|
const outFn = path.join(pluginPath, dstFn);
|
||||||
const wantContents = await fsp.readFile(srcFn, {encoding: 'utf8'});
|
const wantContents = await fsp.readFile(srcFn, {encoding: 'utf8'});
|
||||||
let gotContents = null;
|
let gotContents = null;
|
||||||
|
@ -131,7 +133,7 @@ const logger = log4js.getLogger('checkPlugin');
|
||||||
} catch (err) { /* treat as if the file doesn't exist */ }
|
} catch (err) { /* treat as if the file doesn't exist */ }
|
||||||
try {
|
try {
|
||||||
assert.equal(gotContents, wantContents);
|
assert.equal(gotContents, wantContents);
|
||||||
} catch (err) {
|
} catch (err:any) {
|
||||||
logger.warn(`File ${dstFn} does not match the default`);
|
logger.warn(`File ${dstFn} does not match the default`);
|
||||||
logger.warn(err.message);
|
logger.warn(err.message);
|
||||||
if (!overwrite && gotContents != null) {
|
if (!overwrite && gotContents != null) {
|
||||||
|
@ -238,11 +240,11 @@ const logger = log4js.getLogger('checkPlugin');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fillTemplate = async (templateFilename, outputFilename) => {
|
const fillTemplate = async (templateFilename: string, outputFilename: string) => {
|
||||||
const contents = (await fsp.readFile(templateFilename, 'utf8'))
|
const contents = (await fsp.readFile(templateFilename, 'utf8'))
|
||||||
.replace(/\[name of copyright owner\]/g, execSync('git config user.name'))
|
.replace(/\[name of copyright owner\]/g, execSync('git config user.name'))
|
||||||
.replace(/\[plugin_name\]/g, pluginName)
|
.replace(/\[plugin_name\]/g, pluginName)
|
||||||
.replace(/\[yyyy\]/g, new Date().getFullYear());
|
.replace(/\[yyyy\]/g, new Date().getFullYear().toString());
|
||||||
await fsp.writeFile(outputFilename, contents);
|
await fsp.writeFile(outputFilename, contents);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// Returns a list of stale plugins and their authors email
|
|
||||||
|
|
||||||
const superagent = require('superagent');
|
|
||||||
const currentTime = new Date();
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const res = await superagent.get('https://static.etherpad.org/plugins.full.json');
|
|
||||||
const plugins = JSON.parse(res.text);
|
|
||||||
for (const plugin of Object.keys(plugins)) {
|
|
||||||
const name = plugins[plugin].data.name;
|
|
||||||
const date = new Date(plugins[plugin].time);
|
|
||||||
const diffTime = Math.abs(currentTime - date);
|
|
||||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
||||||
if (diffDays > (365 * 2)) {
|
|
||||||
console.log(`${name}, ${plugins[plugin].data.maintainers[0].email}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
22
bin/plugins/stalePlugins.ts
Normal file
22
bin/plugins/stalePlugins.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Returns a list of stale plugins and their authors email
|
||||||
|
|
||||||
|
import axios from 'axios'
|
||||||
|
const currentTime = new Date();
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const res = await axios.get<string>('https://static.etherpad.org/plugins.full.json');
|
||||||
|
for (const plugin of Object.keys(res.data)) {
|
||||||
|
// @ts-ignore
|
||||||
|
const name = res.data[plugin].data.name;
|
||||||
|
// @ts-ignore
|
||||||
|
const date = new Date(res.data[plugin].time);
|
||||||
|
const diffTime = Math.abs(currentTime.getTime() - date.getTime());
|
||||||
|
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||||
|
if (diffDays > (365 * 2)) {
|
||||||
|
// @ts-ignore
|
||||||
|
console.log(`${name}, ${res.data[plugin].data.maintainers[0].email}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
|
@ -13,16 +13,17 @@ if (process.argv.length !== 4 && process.argv.length !== 5) {
|
||||||
throw new Error('Use: node bin/repairPad.js $PADID $REV [$NEWPADID]');
|
throw new Error('Use: node bin/repairPad.js $PADID $REV [$NEWPADID]');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
const padId = process.argv[2];
|
const padId = process.argv[2];
|
||||||
const newRevHead = process.argv[3];
|
const newRevHead = Number(process.argv[3]);
|
||||||
const newPadId = process.argv[4] || `${padId}-rebuilt`;
|
const newPadId = process.argv[4] || `${padId}-rebuilt`;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const db = require('./src/node/db/DB');
|
const db = require('ep_etherpad-lite/node/db/DB');
|
||||||
await db.init();
|
await db.init();
|
||||||
|
|
||||||
const PadManager = require('./src/node/db/PadManager');
|
const PadManager = require('ep_etherpad-lite/node/db/PadManager');
|
||||||
const Pad = require('./src/node/db/Pad').Pad;
|
const Pad = require('ep_etherpad-lite/node/db/Pad').Pad;
|
||||||
// Validate the newPadId if specified and that a pad with that ID does
|
// Validate the newPadId if specified and that a pad with that ID does
|
||||||
// not already exist to avoid overwriting it.
|
// not already exist to avoid overwriting it.
|
||||||
if (!PadManager.isValidPadId(newPadId)) {
|
if (!PadManager.isValidPadId(newPadId)) {
|
||||||
|
@ -43,8 +44,8 @@ const newPadId = process.argv[4] || `${padId}-rebuilt`;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Rebuild Pad from revisions up to and including the new revision head
|
// Rebuild Pad from revisions up to and including the new revision head
|
||||||
const AuthorManager = require('./src/node/db/AuthorManager');
|
const AuthorManager = require('ep_etherpad-lite/node/db/AuthorManager');
|
||||||
const Changeset = require('./src/static/js/Changeset');
|
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
||||||
// Author attributes are derived from changesets, but there can also be
|
// Author attributes are derived from changesets, but there can also be
|
||||||
// non-author attributes with specific mappings that changesets depend on
|
// non-author attributes with specific mappings that changesets depend on
|
||||||
// and, AFAICT, cannot be recreated any other way
|
// and, AFAICT, cannot be recreated any other way
|
|
@ -4,12 +4,12 @@
|
||||||
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
process.on('unhandledRejection', (err) => { throw err; });
|
||||||
|
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const childProcess = require('child_process');
|
import childProcess from 'child_process';
|
||||||
const log4js = require('log4js');
|
import log4js from 'log4js';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const semver = require('semver');
|
import semver from 'semver';
|
||||||
const {exec} = require('child_process');
|
import {exec} from 'child_process';
|
||||||
|
|
||||||
log4js.configure({appenders: {console: {type: 'console'}},
|
log4js.configure({appenders: {console: {type: 'console'}},
|
||||||
categories: {
|
categories: {
|
||||||
|
@ -33,23 +33,31 @@ if (!release) {
|
||||||
throw new Error('No release type included');
|
throw new Error('No release type included');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (release !== 'patch' && release !== 'minor' && release !== 'major') {
|
||||||
|
console.log(usage);
|
||||||
|
throw new Error('Invalid release type');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const cwd = path.join(fs.realpathSync(__dirname), '../');
|
const cwd = path.join(fs.realpathSync(__dirname), '../');
|
||||||
process.chdir(cwd);
|
process.chdir(cwd);
|
||||||
|
|
||||||
// Run command capturing stdout. Trailing newlines are stripped (like the shell does).
|
// Run command capturing stdout. Trailing newlines are stripped (like the shell does).
|
||||||
const runc =
|
const runc =
|
||||||
(cmd, opts = {}) => childProcess.execSync(cmd, {encoding: 'utf8', ...opts}).replace(/\n+$/, '');
|
(cmd:string, opts = {}) => childProcess.execSync(cmd, {encoding: 'utf8', ...opts}).replace(/\n+$/, '');
|
||||||
// Run command without capturing stdout.
|
// Run command without capturing stdout.
|
||||||
const run = (cmd, opts = {}) => childProcess.execSync(cmd, {stdio: 'inherit', ...opts});
|
const run = (cmd: string, opts = {}) => childProcess.execSync(cmd, {stdio: 'inherit', ...opts});
|
||||||
|
|
||||||
const readJson = (filename) => JSON.parse(fs.readFileSync(filename, {encoding: 'utf8', flag: 'r'}));
|
const readJson = (filename: string) => JSON.parse(fs.readFileSync(filename, {encoding: 'utf8', flag: 'r'}));
|
||||||
const writeJson = (filename, obj) => {
|
const writeJson = (filename: string, obj:object) => {
|
||||||
let json = JSON.stringify(obj, null, 2);
|
let json = JSON.stringify(obj, null, 2);
|
||||||
if (json !== '' && !json.endsWith('\n')) json += '\n';
|
if (json !== '' && !json.endsWith('\n')) json += '\n';
|
||||||
fs.writeFileSync(filename, json);
|
fs.writeFileSync(filename, json);
|
||||||
};
|
};
|
||||||
|
|
||||||
const assertWorkDirClean = (opts = {}) => {
|
const assertWorkDirClean = (opts:{
|
||||||
|
cwd?: string;
|
||||||
|
} = {}) => {
|
||||||
opts.cwd = runc('git rev-parse --show-cdup', opts) || cwd;
|
opts.cwd = runc('git rev-parse --show-cdup', opts) || cwd;
|
||||||
const m = runc('git diff-files --name-status', opts);
|
const m = runc('git diff-files --name-status', opts);
|
||||||
if (m !== '') throw new Error(`modifications in working directory ${opts.cwd}:\n${m}`);
|
if (m !== '') throw new Error(`modifications in working directory ${opts.cwd}:\n${m}`);
|
||||||
|
@ -59,7 +67,9 @@ const assertWorkDirClean = (opts = {}) => {
|
||||||
if (s !== '') throw new Error(`uncommitted changes in working directory ${opts.cwd}:\n${s}`);
|
if (s !== '') throw new Error(`uncommitted changes in working directory ${opts.cwd}:\n${s}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const assertBranchCheckedOut = (branch, opts = {}) => {
|
const assertBranchCheckedOut = (branch: string, opts:{
|
||||||
|
cwd?: string;
|
||||||
|
} = {}) => {
|
||||||
const b = runc('git symbolic-ref HEAD', opts);
|
const b = runc('git symbolic-ref HEAD', opts);
|
||||||
if (b !== `refs/heads/${branch}`) {
|
if (b !== `refs/heads/${branch}`) {
|
||||||
const d = opts.cwd ? path.resolve(cwd, opts.cwd) : cwd;
|
const d = opts.cwd ? path.resolve(cwd, opts.cwd) : cwd;
|
||||||
|
@ -67,21 +77,23 @@ const assertBranchCheckedOut = (branch, opts = {}) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const assertUpstreamOk = (branch, opts = {}) => {
|
const assertUpstreamOk = (branch: string, opts:{
|
||||||
|
cwd?: string;
|
||||||
|
} = {}) => {
|
||||||
const upstream = runc(`git rev-parse --symbolic-full-name ${branch}@{u}`, opts);
|
const upstream = runc(`git rev-parse --symbolic-full-name ${branch}@{u}`, opts);
|
||||||
if (!(new RegExp(`^refs/remotes/[^/]+/${branch}`)).test(upstream)) {
|
if (!(new RegExp(`^refs/remotes/[^/]+/${branch}`)).test(upstream)) {
|
||||||
throw new Error(`${branch} should track origin/${branch}; see git branch --set-upstream-to`);
|
throw new Error(`${branch} should track origin/${branch}; see git branch --set-upstream-to`);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
run(`git merge-base --is-ancestor ${branch} ${branch}@{u}`);
|
run(`git merge-base --is-ancestor ${branch} ${branch}@{u}`);
|
||||||
} catch (err) {
|
} catch (err:any) {
|
||||||
if (err.status !== 1) throw err;
|
if (err.status !== 1) throw err;
|
||||||
throw new Error(`${branch} is ahead of origin/${branch}; do you need to push?`);
|
throw new Error(`${branch} is ahead of origin/${branch}; do you need to push?`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if asciidoctor is installed
|
// Check if asciidoctor is installed
|
||||||
exec('asciidoctor -v', (err, stdout) => {
|
exec('asciidoctor -v', (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log('Please install asciidoctor');
|
console.log('Please install asciidoctor');
|
||||||
console.log('https://asciidoctor.org/docs/install-toolchain/');
|
console.log('https://asciidoctor.org/docs/install-toolchain/');
|
||||||
|
@ -89,10 +101,10 @@ exec('asciidoctor -v', (err, stdout) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const dirExists = (dir) => {
|
const dirExists = (dir: string) => {
|
||||||
try {
|
try {
|
||||||
return fs.statSync(dir).isDirectory();
|
return fs.statSync(dir).isDirectory();
|
||||||
} catch (err) {
|
} catch (err:any) {
|
||||||
if (err.code !== 'ENOENT') throw err;
|
if (err.code !== 'ENOENT') throw err;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -165,7 +177,7 @@ try {
|
||||||
run('git checkout develop');
|
run('git checkout develop');
|
||||||
console.log('Merging master into develop...');
|
console.log('Merging master into develop...');
|
||||||
run('git merge --no-ff --no-edit master');
|
run('git merge --no-ff --no-edit master');
|
||||||
} catch (err) {
|
} catch (err:any) {
|
||||||
console.error(err.toString());
|
console.error(err.toString());
|
||||||
console.warn('Resetting repository...');
|
console.warn('Resetting repository...');
|
||||||
console.warn('Resetting master...');
|
console.warn('Resetting master...');
|
||||||
|
@ -192,7 +204,7 @@ try {
|
||||||
run(`npm version ${newVersion}`, {cwd: '../ether.github.com'});
|
run(`npm version ${newVersion}`, {cwd: '../ether.github.com'});
|
||||||
run('git add .', {cwd: '../ether.github.com/'});
|
run('git add .', {cwd: '../ether.github.com/'});
|
||||||
run(`git commit -m '${newVersion} docs'`, {cwd: '../ether.github.com/'});
|
run(`git commit -m '${newVersion} docs'`, {cwd: '../ether.github.com/'});
|
||||||
} catch (err) {
|
} catch (err:any) {
|
||||||
console.error(err.toString());
|
console.error(err.toString());
|
||||||
console.warn('Resetting repository...');
|
console.warn('Resetting repository...');
|
||||||
console.warn('Resetting master...');
|
console.warn('Resetting master...');
|
|
@ -1,5 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import process from "process";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a repair tool. It extracts all datas of a pad, removes and inserts them again.
|
* This is a repair tool. It extracts all datas of a pad, removes and inserts them again.
|
||||||
*/
|
*/
|
||||||
|
@ -19,19 +21,19 @@ let valueCount = 0;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
// initialize database
|
// initialize database
|
||||||
require('./src/node/utils/Settings');
|
require('ep_etherpad-lite/node/utils/Settings');
|
||||||
const db = require('./src/node/db/DB');
|
const db = require('ep_etherpad-lite/node/db/DB');
|
||||||
await db.init();
|
await db.init();
|
||||||
|
|
||||||
// get the pad
|
// get the pad
|
||||||
const padManager = require('./src/node/db/PadManager');
|
const padManager = require('ep_etherpad-lite/node/db/PadManager');
|
||||||
const pad = await padManager.getPad(padId);
|
const pad = await padManager.getPad(padId);
|
||||||
|
|
||||||
// accumulate the required keys
|
// accumulate the required keys
|
||||||
const neededDBValues = [`pad:${padId}`];
|
const neededDBValues = [`pad:${padId}`];
|
||||||
|
|
||||||
// add all authors
|
// add all authors
|
||||||
neededDBValues.push(...pad.getAllAuthors().map((author) => `globalAuthor:${author}`));
|
neededDBValues.push(...pad.getAllAuthors().map((author: string) => `globalAuthor:${author}`));
|
||||||
|
|
||||||
// add all revisions
|
// add all revisions
|
||||||
for (let rev = 0; rev <= pad.head; ++rev) {
|
for (let rev = 0; rev <= pad.head; ++rev) {
|
109
bin/tsconfig.json
Normal file
109
bin/tsconfig.json
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||||
|
|
||||||
|
/* Projects */
|
||||||
|
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||||
|
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||||
|
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||||
|
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||||
|
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||||
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||||
|
|
||||||
|
/* Language and Environment */
|
||||||
|
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||||
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||||
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||||
|
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
|
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||||
|
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||||
|
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||||
|
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||||
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||||
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||||
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||||
|
|
||||||
|
/* Modules */
|
||||||
|
"module": "commonjs", /* Specify what module code is generated. */
|
||||||
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||||
|
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||||
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
|
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||||
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
|
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||||
|
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||||
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||||
|
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||||
|
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||||
|
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||||
|
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||||
|
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||||
|
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||||
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
|
/* JavaScript Support */
|
||||||
|
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||||
|
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||||
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||||
|
|
||||||
|
/* Emit */
|
||||||
|
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||||
|
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||||
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||||
|
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||||
|
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||||
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||||
|
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||||
|
// "removeComments": true, /* Disable emitting comments. */
|
||||||
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||||
|
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||||
|
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||||
|
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||||
|
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||||
|
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||||
|
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||||
|
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||||
|
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||||
|
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||||
|
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||||
|
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||||
|
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||||
|
|
||||||
|
/* Interop Constraints */
|
||||||
|
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||||
|
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||||
|
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||||
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||||
|
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||||
|
|
||||||
|
/* Type Checking */
|
||||||
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
|
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||||
|
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||||
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||||
|
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||||
|
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||||
|
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||||
|
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||||
|
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||||
|
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||||
|
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||||
|
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||||
|
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||||
|
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||||
|
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||||
|
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||||
|
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||||
|
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||||
|
|
||||||
|
/* Completeness */
|
||||||
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||||
|
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,4 +8,4 @@ OUTDATED=$(npm outdated --depth=0 | awk '{print $1}' | grep '^ep_') || {
|
||||||
}
|
}
|
||||||
set -- ${OUTDATED}
|
set -- ${OUTDATED}
|
||||||
echo "Updating plugins: $*"
|
echo "Updating plugins: $*"
|
||||||
exec npm install --no-save "$@"
|
exec pnpm install "$@"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
packages:
|
packages:
|
||||||
- src
|
- src
|
||||||
- admin
|
- admin
|
||||||
|
- bin
|
||||||
|
|
Loading…
Reference in a new issue