@ -1,12 +1,12 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- node_modules
|
||||||
node_js:
|
node_js:
|
||||||
- node
|
- node
|
||||||
before_install:
|
before_install:
|
||||||
- npm install -g semistandard
|
- npm install -g semistandard
|
||||||
- npm install -g prettier
|
- npm install -g prettier
|
||||||
- npm install webber
|
|
||||||
- npm install builder
|
|
||||||
- npm install tagger
|
|
||||||
script:
|
script:
|
||||||
- npm run tagger
|
- npm run tagger
|
||||||
- npm run linter
|
- npm run linter
|
||||||
|
|||||||
1741
docs/index.html
1741
docs/index.html
File diff suppressed because one or more lines are too long
1595
package-lock.json
generated
1595
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
33
package.json
33
package.json
@ -1,43 +1,30 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"builder": "^3.2.3",
|
|
||||||
"concurrently": "^3.5.1",
|
|
||||||
"fs-extra": "^4.0.2",
|
"fs-extra": "^4.0.2",
|
||||||
"html-minifier": "^3.5.7",
|
"html-minifier": "^3.5.7",
|
||||||
"live-server": "^1.2.0",
|
|
||||||
"markdown-it": "^8.4.0",
|
"markdown-it": "^8.4.0",
|
||||||
"node-sass": "^4.7.2",
|
"node-sass": "^4.7.2",
|
||||||
"nodemon": "^1.12.1",
|
|
||||||
"prettier": "^1.9.2",
|
"prettier": "^1.9.2",
|
||||||
"semistandard": "^11.0.0",
|
"semistandard": "^11.0.0",
|
||||||
"tagger": "^0.1.2",
|
|
||||||
"webber": "0.0.1"
|
|
||||||
},
|
|
||||||
"name": "30-seconds-of-code",
|
|
||||||
"description": "A collection of useful Javascript snippets.",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"main": "index.js",
|
|
||||||
"devDependencies": {
|
|
||||||
"chalk": "^2.3.0",
|
"chalk": "^2.3.0",
|
||||||
"tape": "^4.8.0"
|
"tape": "^4.8.0"
|
||||||
},
|
},
|
||||||
|
"name": "30-seconds-of-code",
|
||||||
|
"description": "A collection of useful JavaScript snippets.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"builder": "node ./scripts/build-script.js",
|
"builder": "node ./scripts/build.js",
|
||||||
"linter": "node ./scripts/lint-script.js",
|
"linter": "node ./scripts/lint.js",
|
||||||
"tagger": "node ./scripts/tag-script.js",
|
"tagger": "node ./scripts/tag.js",
|
||||||
"webber": "node ./scripts/web-script.js",
|
"webber": "node ./scripts/web.js",
|
||||||
"tdd": "node ./scripts/tdd-script.js",
|
"tdd": "node ./scripts/tdd.js"
|
||||||
"start": "concurrently --kill-others \"nodemon -e js,md -i README.md -x \\\"npm run build-list\\\"\" \"live-server ./build\""
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/Chalarangelo/30-seconds-of-code.git"
|
"url": "git+https://github.com/Chalarangelo/30-seconds-of-code.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": ["javascript", "snippets", "list"],
|
||||||
"javascript",
|
|
||||||
"snippets",
|
|
||||||
"list"
|
|
||||||
],
|
|
||||||
"author": "Chalarangelo (chalarangelo@gmail.com)",
|
"author": "Chalarangelo (chalarangelo@gmail.com)",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
|||||||
@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
This is the builder script that generates the README file.
|
|
||||||
Run using `npm run builder`.
|
|
||||||
*/
|
|
||||||
// Load modules
|
|
||||||
const fs = require('fs-extra'), path = require('path'), chalk = require('chalk');
|
|
||||||
// Set variables for paths
|
|
||||||
const snippetsPath = './snippets', staticPartsPath = './static-parts';
|
|
||||||
// Set variables for script
|
|
||||||
let snippets = {}, startPart = '', endPart = '', output = '', tagDbData = {};
|
|
||||||
// Load helper functions (these are from existing snippets in 30 seconds of code!)
|
|
||||||
const objectFromPairs = arr => arr.reduce((a, v) => (a[v[0]] = v[1], a), {});
|
|
||||||
const capitalize = (str, lowerRest = false) => str.slice(0, 1).toUpperCase() + (lowerRest ? str.slice(1).toLowerCase() : str.slice(1));
|
|
||||||
// Start the timer of the script
|
|
||||||
console.time('Builder');
|
|
||||||
// Synchronously read all snippets and sort them as necessary (case-insensitive)
|
|
||||||
try {
|
|
||||||
let snippetFilenames = fs.readdirSync(snippetsPath);
|
|
||||||
snippetFilenames.sort((a, b) => {
|
|
||||||
a = a.toLowerCase();
|
|
||||||
b = b.toLowerCase();
|
|
||||||
if (a < b) return -1;
|
|
||||||
if (a > b) return 1;
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
// Store the data read from each snippet in the appropriate object
|
|
||||||
for(let snippet of snippetFilenames) snippets[snippet] = fs.readFileSync(path.join(snippetsPath,snippet),'utf8');
|
|
||||||
}
|
|
||||||
catch (err){ // Handle errors (hopefully not!)
|
|
||||||
console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
// Load static parts for the README file
|
|
||||||
try {
|
|
||||||
startPart = fs.readFileSync(path.join(staticPartsPath,'README-start.md'),'utf8');
|
|
||||||
endPart = fs.readFileSync(path.join(staticPartsPath,'README-end.md'),'utf8');
|
|
||||||
}
|
|
||||||
catch (err){ // Handle errors (hopefully not!)
|
|
||||||
console.log(`${chalk.red('ERROR!')} During static part loading: ${err}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
// Load tag data from the database
|
|
||||||
try {
|
|
||||||
tagDbData = objectFromPairs(fs.readFileSync('tag_database','utf8').split('\n').slice(0,-1).map(v => v.split(':').slice(0,2)));
|
|
||||||
}
|
|
||||||
catch (err){ // Handle errors (hopefully not!)
|
|
||||||
console.log(`${chalk.red('ERROR!')} During tag database loading: ${err}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
// Create the output for the README file
|
|
||||||
try {
|
|
||||||
// Add the start static part
|
|
||||||
output += `${startPart+'\n'}`;
|
|
||||||
// Loop over tags and snippets to create the table of contents
|
|
||||||
let uncategorizedOutput = '';
|
|
||||||
for(let tag of [...new Set(Object.entries(tagDbData).map(t => t[1]))].filter(v => v).sort((a,b) => a.localeCompare(b))){
|
|
||||||
if(capitalize(tag, true)=='Uncategorized') {
|
|
||||||
uncategorizedOutput +=`### _${capitalize(tag, true)}_\n\n<details>\n<summary>View contents</summary>\n\n`;
|
|
||||||
for(let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
|
||||||
uncategorizedOutput += `* [\`${taggedSnippet[0]}\`](#${taggedSnippet[0].toLowerCase()})\n`
|
|
||||||
uncategorizedOutput += '\n</details>\n\n';
|
|
||||||
} else {
|
|
||||||
output +=`### ${capitalize(tag, true)}\n\n<details>\n<summary>View contents</summary>\n\n`;
|
|
||||||
for(let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
|
||||||
output += `* [\`${taggedSnippet[0]}\`](#${taggedSnippet[0].toLowerCase()})\n`
|
|
||||||
output += '\n</details>\n\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output += uncategorizedOutput;
|
|
||||||
uncategorizedOutput = '';
|
|
||||||
// Loop over tags and snippets to create the list of snippets
|
|
||||||
for(let tag of [...new Set(Object.entries(tagDbData).map(t => t[1]))].filter(v => v).sort((a,b) => a.localeCompare(b))){
|
|
||||||
if(capitalize(tag, true)=='Uncategorized') {
|
|
||||||
uncategorizedOutput +=`## _${capitalize(tag, true)}_\n`;
|
|
||||||
for(let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
|
||||||
uncategorizedOutput += `\n${snippets[taggedSnippet[0]+'.md']+'\n<br>[⬆ back to top](#table-of-contents)\n\n'}`;
|
|
||||||
} else {
|
|
||||||
output +=`## ${capitalize(tag, true)}\n`;
|
|
||||||
for(let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag)){
|
|
||||||
let data = snippets[taggedSnippet[0]+'.md'];
|
|
||||||
data = data.slice(0,data.lastIndexOf('```js')) + '<details>\n<summary>Examples</summary>\n\n' + data.slice(data.lastIndexOf('```js'),data.lastIndexOf('```')) + data.slice(data.lastIndexOf('```')) + '\n</details>\n';
|
|
||||||
output += `\n${data+'\n\n[⬆ Back to top](#table-of-contents)\n\n'}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output += uncategorizedOutput;
|
|
||||||
// Add the ending static part
|
|
||||||
output += `\n${endPart+'\n'}`;
|
|
||||||
// Write to the README file
|
|
||||||
fs.writeFileSync('README.md', output);
|
|
||||||
}
|
|
||||||
catch (err){ // Handle errors (hopefully not!)
|
|
||||||
console.log(`${chalk.red('ERROR!')} During README generation: ${err}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
// Log a success message
|
|
||||||
console.log(`${chalk.green('SUCCESS!')} README file generated!`);
|
|
||||||
// Log the time taken
|
|
||||||
console.timeEnd('Builder');
|
|
||||||
127
scripts/build.js
Normal file
127
scripts/build.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
This is the builder script that generates the README file.
|
||||||
|
Run using `npm run builder`.
|
||||||
|
*/
|
||||||
|
// Load modules
|
||||||
|
const fs = require('fs-extra'),
|
||||||
|
path = require('path'),
|
||||||
|
chalk = require('chalk');
|
||||||
|
// Set variables for paths
|
||||||
|
const snippetsPath = './snippets',
|
||||||
|
staticPartsPath = './static-parts';
|
||||||
|
// Set variables for script
|
||||||
|
let snippets = {},
|
||||||
|
startPart = '',
|
||||||
|
endPart = '',
|
||||||
|
output = '',
|
||||||
|
tagDbData = {};
|
||||||
|
// Load helper functions (these are from existing snippets in 30 seconds of code!)
|
||||||
|
const objectFromPairs = arr => arr.reduce((a, v) => ((a[v[0]] = v[1]), a), {});
|
||||||
|
const capitalize = (str, lowerRest = false) =>
|
||||||
|
str.slice(0, 1).toUpperCase() + (lowerRest ? str.slice(1).toLowerCase() : str.slice(1));
|
||||||
|
// Start the timer of the script
|
||||||
|
console.time('Builder');
|
||||||
|
// Synchronously read all snippets and sort them as necessary (case-insensitive)
|
||||||
|
try {
|
||||||
|
let snippetFilenames = fs.readdirSync(snippetsPath);
|
||||||
|
snippetFilenames.sort((a, b) => {
|
||||||
|
a = a.toLowerCase();
|
||||||
|
b = b.toLowerCase();
|
||||||
|
if (a < b) return -1;
|
||||||
|
if (a > b) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
// Store the data read from each snippet in the appropriate object
|
||||||
|
for (let snippet of snippetFilenames)
|
||||||
|
snippets[snippet] = fs.readFileSync(path.join(snippetsPath, snippet), 'utf8');
|
||||||
|
} catch (err) {
|
||||||
|
// Handle errors (hopefully not!)
|
||||||
|
console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
// Load static parts for the README file
|
||||||
|
try {
|
||||||
|
startPart = fs.readFileSync(path.join(staticPartsPath, 'README-start.md'), 'utf8');
|
||||||
|
endPart = fs.readFileSync(path.join(staticPartsPath, 'README-end.md'), 'utf8');
|
||||||
|
} catch (err) {
|
||||||
|
// Handle errors (hopefully not!)
|
||||||
|
console.log(`${chalk.red('ERROR!')} During static part loading: ${err}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
// Load tag data from the database
|
||||||
|
try {
|
||||||
|
tagDbData = objectFromPairs(
|
||||||
|
fs
|
||||||
|
.readFileSync('tag_database', 'utf8')
|
||||||
|
.split('\n')
|
||||||
|
.slice(0, -1)
|
||||||
|
.map(v => v.split(':').slice(0, 2))
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
// Handle errors (hopefully not!)
|
||||||
|
console.log(`${chalk.red('ERROR!')} During tag database loading: ${err}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
// Create the output for the README file
|
||||||
|
try {
|
||||||
|
// Add the start static part
|
||||||
|
output += `${startPart + '\n'}`;
|
||||||
|
// Loop over tags and snippets to create the table of contents
|
||||||
|
let uncategorizedOutput = '';
|
||||||
|
for (let tag of [...new Set(Object.entries(tagDbData).map(t => t[1]))]
|
||||||
|
.filter(v => v)
|
||||||
|
.sort((a, b) => a.localeCompare(b))) {
|
||||||
|
if (capitalize(tag, true) == 'Uncategorized') {
|
||||||
|
uncategorizedOutput += `### _${capitalize(
|
||||||
|
tag,
|
||||||
|
true
|
||||||
|
)}_\n\n<details>\n<summary>View contents</summary>\n\n`;
|
||||||
|
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
||||||
|
uncategorizedOutput += `* [\`${taggedSnippet[0]}\`](#${taggedSnippet[0].toLowerCase()})\n`;
|
||||||
|
uncategorizedOutput += '\n</details>\n\n';
|
||||||
|
} else {
|
||||||
|
output += `### ${capitalize(tag, true)}\n\n<details>\n<summary>View contents</summary>\n\n`;
|
||||||
|
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
||||||
|
output += `* [\`${taggedSnippet[0]}\`](#${taggedSnippet[0].toLowerCase()})\n`;
|
||||||
|
output += '\n</details>\n\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output += uncategorizedOutput;
|
||||||
|
uncategorizedOutput = '';
|
||||||
|
// Loop over tags and snippets to create the list of snippets
|
||||||
|
for (let tag of [...new Set(Object.entries(tagDbData).map(t => t[1]))]
|
||||||
|
.filter(v => v)
|
||||||
|
.sort((a, b) => a.localeCompare(b))) {
|
||||||
|
if (capitalize(tag, true) == 'Uncategorized') {
|
||||||
|
uncategorizedOutput += `## _${capitalize(tag, true)}_\n`;
|
||||||
|
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
||||||
|
uncategorizedOutput += `\n${snippets[taggedSnippet[0] + '.md'] +
|
||||||
|
'\n<br>[⬆ back to top](#table-of-contents)\n\n'}`;
|
||||||
|
} else {
|
||||||
|
output += `## ${capitalize(tag, true)}\n`;
|
||||||
|
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag)) {
|
||||||
|
let data = snippets[taggedSnippet[0] + '.md'];
|
||||||
|
data =
|
||||||
|
data.slice(0, data.lastIndexOf('```js')) +
|
||||||
|
'<details>\n<summary>Examples</summary>\n\n' +
|
||||||
|
data.slice(data.lastIndexOf('```js'), data.lastIndexOf('```')) +
|
||||||
|
data.slice(data.lastIndexOf('```')) +
|
||||||
|
'\n</details>\n';
|
||||||
|
output += `\n${data + '\n<br>[⬆ Back to top](#table-of-contents)\n\n'}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output += uncategorizedOutput;
|
||||||
|
// Add the ending static part
|
||||||
|
output += `\n${endPart + '\n'}`;
|
||||||
|
// Write to the README file
|
||||||
|
fs.writeFileSync('README.md', output);
|
||||||
|
} catch (err) {
|
||||||
|
// Handle errors (hopefully not!)
|
||||||
|
console.log(`${chalk.red('ERROR!')} During README generation: ${err}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
// Log a success message
|
||||||
|
console.log(`${chalk.green('SUCCESS!')} README file generated!`);
|
||||||
|
// Log the time taken
|
||||||
|
console.timeEnd('Builder');
|
||||||
@ -18,7 +18,8 @@ const codeRE = /```\s*js([\s\S]*?)```/g;
|
|||||||
console.time('Linter');
|
console.time('Linter');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const snippets = fs.readdirSync(SNIPPETS_PATH)
|
const snippets = fs
|
||||||
|
.readdirSync(SNIPPETS_PATH)
|
||||||
.sort((a, b) => a.toLowerCase() - b.toLowerCase())
|
.sort((a, b) => a.toLowerCase() - b.toLowerCase())
|
||||||
// turn it into an object so we can add data to it to be used in a different scope
|
// turn it into an object so we can add data to it to be used in a different scope
|
||||||
.map(name => ({ name }));
|
.map(name => ({ name }));
|
||||||
@ -46,7 +47,8 @@ try {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const cmd = `semistandard "${TEMP_PATH}" --fix & ` +
|
const cmd =
|
||||||
|
`semistandard "${TEMP_PATH}" --fix & ` +
|
||||||
`prettier "${TEMP_PATH}/*.js" --single-quote --print-width=100 --write`;
|
`prettier "${TEMP_PATH}/*.js" --single-quote --print-width=100 --write`;
|
||||||
|
|
||||||
cp.exec(cmd, {}, (err, stdout, stderr) => {
|
cp.exec(cmd, {}, (err, stdout, stderr) => {
|
||||||
@ -3,14 +3,20 @@
|
|||||||
Run using `npm run tagger`.
|
Run using `npm run tagger`.
|
||||||
*/
|
*/
|
||||||
// Load modules
|
// Load modules
|
||||||
const fs = require('fs-extra'), path = require('path'), chalk = require('chalk');
|
const fs = require('fs-extra'),
|
||||||
|
path = require('path'),
|
||||||
|
chalk = require('chalk');
|
||||||
// Set variables for paths
|
// Set variables for paths
|
||||||
const snippetsPath = './snippets';
|
const snippetsPath = './snippets';
|
||||||
// Set variables for script
|
// Set variables for script
|
||||||
let snippets = {}, output = '', tagDbData = {}, missingTags = 0, tagDbStats = {};
|
let snippets = {},
|
||||||
|
output = '',
|
||||||
|
tagDbData = {},
|
||||||
|
missingTags = 0,
|
||||||
|
tagDbStats = {};
|
||||||
// Load helper functions (these are from existing snippets in 30 seconds of code!)
|
// Load helper functions (these are from existing snippets in 30 seconds of code!)
|
||||||
const objectFromPairs = arr => arr.reduce((a, v) => (a[v[0]] = v[1], a), {});
|
const objectFromPairs = arr => arr.reduce((a, v) => ((a[v[0]] = v[1]), a), {});
|
||||||
const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0);
|
const countOccurrences = (arr, value) => arr.reduce((a, v) => (v === value ? a + 1 : a + 0), 0);
|
||||||
// Start the timer of the script
|
// Start the timer of the script
|
||||||
console.time('Tagger');
|
console.time('Tagger');
|
||||||
// Synchronously read all snippets and sort them as necessary (case-insensitive)
|
// Synchronously read all snippets and sort them as necessary (case-insensitive)
|
||||||
@ -24,43 +30,60 @@ try {
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
// Store the data read from each snippet in the appropriate object
|
// Store the data read from each snippet in the appropriate object
|
||||||
for(let snippet of snippetFilenames) snippets[snippet] = fs.readFileSync(path.join(snippetsPath,snippet),'utf8');
|
for (let snippet of snippetFilenames)
|
||||||
}
|
snippets[snippet] = fs.readFileSync(path.join(snippetsPath, snippet), 'utf8');
|
||||||
catch (err){ // Handle errors (hopefully not!)
|
} catch (err) {
|
||||||
|
// Handle errors (hopefully not!)
|
||||||
console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
|
console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
// Load tag data from the database
|
// Load tag data from the database
|
||||||
try {
|
try {
|
||||||
tagDbData = objectFromPairs(fs.readFileSync('tag_database','utf8').split('\n').slice(0,-1).map(v => v.split(':').slice(0,2)));
|
tagDbData = objectFromPairs(
|
||||||
tagDbStats = Object.entries(tagDbData).sort((a,b) => a[1].localeCompare(b[1])).reduce((acc, val) => {acc.hasOwnProperty(val[1]) ? acc[val[1]]++ : acc[val[1]] = 1; return acc;}, {});
|
fs
|
||||||
}
|
.readFileSync('tag_database', 'utf8')
|
||||||
catch (err){ // Handle errors (hopefully not!)
|
.split('\n')
|
||||||
|
.slice(0, -1)
|
||||||
|
.map(v => v.split(':').slice(0, 2))
|
||||||
|
);
|
||||||
|
tagDbStats = Object.entries(tagDbData)
|
||||||
|
.sort((a, b) => a[1].localeCompare(b[1]))
|
||||||
|
.reduce((acc, val) => {
|
||||||
|
acc.hasOwnProperty(val[1]) ? acc[val[1]]++ : (acc[val[1]] = 1);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
} catch (err) {
|
||||||
|
// Handle errors (hopefully not!)
|
||||||
console.log(`${chalk.red('ERROR!')} During tag database loading: ${err}`);
|
console.log(`${chalk.red('ERROR!')} During tag database loading: ${err}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
// Update the listing of snippets in tag_database and log the statistics, along with missing scripts
|
// Update the listing of snippets in tag_database and log the statistics, along with missing scripts
|
||||||
try {
|
try {
|
||||||
for(let snippet of Object.entries(snippets))
|
for (let snippet of Object.entries(snippets))
|
||||||
if(tagDbData.hasOwnProperty(snippet[0].slice(0,-3)) && tagDbData[snippet[0].slice(0,-3)].trim())
|
if (
|
||||||
output += `${snippet[0].slice(0,-3)}:${tagDbData[snippet[0].slice(0,-3)].trim()}\n`;
|
tagDbData.hasOwnProperty(snippet[0].slice(0, -3)) &&
|
||||||
|
tagDbData[snippet[0].slice(0, -3)].trim()
|
||||||
|
)
|
||||||
|
output += `${snippet[0].slice(0, -3)}:${tagDbData[snippet[0].slice(0, -3)].trim()}\n`;
|
||||||
else {
|
else {
|
||||||
output += `${snippet[0].slice(0,-3)}:uncategorized\n`;
|
output += `${snippet[0].slice(0, -3)}:uncategorized\n`;
|
||||||
missingTags++;
|
missingTags++;
|
||||||
console.log(`${chalk.yellow('Tagged uncategorized:')} ${snippet[0].slice(0,-3)}`);
|
console.log(`${chalk.yellow('Tagged uncategorized:')} ${snippet[0].slice(0, -3)}`);
|
||||||
}
|
}
|
||||||
// Write to tag_database
|
// Write to tag_database
|
||||||
fs.writeFileSync('tag_database', output);
|
fs.writeFileSync('tag_database', output);
|
||||||
}
|
} catch (err) {
|
||||||
catch (err){ // Handle errors (hopefully not!)
|
// Handle errors (hopefully not!)
|
||||||
console.log(`${chalk.red('ERROR!')} During tag_database generation: ${err}`);
|
console.log(`${chalk.red('ERROR!')} During tag_database generation: ${err}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
// Log statistics for the tag_database file
|
// Log statistics for the tag_database file
|
||||||
console.log(`\n${chalk.bgWhite(chalk.black('=== TAG STATS ==='))}`)
|
console.log(`\n${chalk.bgWhite(chalk.black('=== TAG STATS ==='))}`);
|
||||||
for(let tagData of Object.entries(tagDbStats).filter(v => v[0] !== 'undefined'))
|
for (let tagData of Object.entries(tagDbStats).filter(v => v[0] !== 'undefined'))
|
||||||
console.log(`${chalk.green(tagData[0])}: ${tagData[1]} snippets`);
|
console.log(`${chalk.green(tagData[0])}: ${tagData[1]} snippets`);
|
||||||
console.log(`${chalk.blue('New untagged snippets (will be tagged as \'uncategorized\'):')} ${missingTags}\n`);
|
console.log(
|
||||||
|
`${chalk.blue("New untagged snippets (will be tagged as 'uncategorized'):")} ${missingTags}\n`
|
||||||
|
);
|
||||||
// Log a success message
|
// Log a success message
|
||||||
console.log(`${chalk.green('SUCCESS!')} tag_database file updated!`);
|
console.log(`${chalk.green('SUCCESS!')} tag_database file updated!`);
|
||||||
// Log the time taken
|
// Log the time taken
|
||||||
@ -1,41 +0,0 @@
|
|||||||
const fs = require('fs-extra');
|
|
||||||
|
|
||||||
const SNIPPETS_PATH = './snippets';
|
|
||||||
const TEST_PATH = './test';
|
|
||||||
|
|
||||||
const snippetFiles = fs.readdirSync(SNIPPETS_PATH, 'utf8')
|
|
||||||
.map(fileName => fileName.slice(0, -3));
|
|
||||||
|
|
||||||
fs.removeSync(TEST_PATH);
|
|
||||||
|
|
||||||
snippetFiles
|
|
||||||
.map(fileName => { fs.ensureDirSync(`${TEST_PATH}/${fileName}`); return fileName})
|
|
||||||
.map(fileName => {
|
|
||||||
|
|
||||||
const fileData = fs.readFileSync(`${SNIPPETS_PATH}/${fileName}.md`, 'utf8');
|
|
||||||
const fileCode = fileData.slice( fileData.indexOf('```js'), fileData.lastIndexOf('```') + 3 );
|
|
||||||
const blockMarkers = fileCode.split('\n').map((line, lineIndex) => line.slice(0, 3) === '```' ? lineIndex : '//CLEAR//').filter(x => !(x === '//CLEAR//'))
|
|
||||||
const fileFunction = fileCode.split('\n').map(line => line.trim()).filter((_, i) => blockMarkers[0] < i && i < blockMarkers[1]);
|
|
||||||
const fileExample = fileCode.split('\n').map(line => line.trim()).filter((_, i) => blockMarkers[2] < i && i < blockMarkers[3]);
|
|
||||||
|
|
||||||
const exportFile = `module.exports = ${fileFunction.join('\n').slice(17)}`;
|
|
||||||
const exportTest = [
|
|
||||||
`const test = require('tape');`,
|
|
||||||
`const ${fileName} = require('./${fileName}.js');`,
|
|
||||||
`test('Testing ${fileName}', (t) => {`,
|
|
||||||
`//For more information on all the methods supported by tape\n//Please go to https://github.com/substack/tape`,
|
|
||||||
`//t.deepEqual(${fileName}(args..), 'Expected');`,
|
|
||||||
`//t.equal(${fileName}(args..), 'Expected');`,
|
|
||||||
`//t.false(${fileName}(args..), 'Expected');`,
|
|
||||||
`//t.true(${fileName}(args..), 'Expected');`,
|
|
||||||
`//t.throws(${fileName}(args..), 'Expected');`,
|
|
||||||
`t.end();`,
|
|
||||||
`});`
|
|
||||||
].join('\n')
|
|
||||||
|
|
||||||
|
|
||||||
fs.writeFileSync(`${TEST_PATH}/${fileName}/${fileName}.js`, exportFile);
|
|
||||||
fs.writeFileSync(`${TEST_PATH}/${fileName}/${fileName}.test.js`, exportTest);
|
|
||||||
|
|
||||||
return fileName;
|
|
||||||
})
|
|
||||||
50
scripts/tdd.js
Normal file
50
scripts/tdd.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
const fs = require('fs-extra');
|
||||||
|
|
||||||
|
const SNIPPETS_PATH = './snippets';
|
||||||
|
const TEST_PATH = './test';
|
||||||
|
|
||||||
|
const snippetFiles = fs.readdirSync(SNIPPETS_PATH, 'utf8').map(fileName => fileName.slice(0, -3));
|
||||||
|
|
||||||
|
fs.removeSync(TEST_PATH);
|
||||||
|
|
||||||
|
snippetFiles
|
||||||
|
.map(fileName => {
|
||||||
|
fs.ensureDirSync(`${TEST_PATH}/${fileName}`);
|
||||||
|
return fileName;
|
||||||
|
})
|
||||||
|
.map(fileName => {
|
||||||
|
const fileData = fs.readFileSync(`${SNIPPETS_PATH}/${fileName}.md`, 'utf8');
|
||||||
|
const fileCode = fileData.slice(fileData.indexOf('```js'), fileData.lastIndexOf('```') + 3);
|
||||||
|
const blockMarkers = fileCode
|
||||||
|
.split('\n')
|
||||||
|
.map((line, lineIndex) => (line.slice(0, 3) === '```' ? lineIndex : '//CLEAR//'))
|
||||||
|
.filter(x => !(x === '//CLEAR//'));
|
||||||
|
const fileFunction = fileCode
|
||||||
|
.split('\n')
|
||||||
|
.map(line => line.trim())
|
||||||
|
.filter((_, i) => blockMarkers[0] < i && i < blockMarkers[1]);
|
||||||
|
const fileExample = fileCode
|
||||||
|
.split('\n')
|
||||||
|
.map(line => line.trim())
|
||||||
|
.filter((_, i) => blockMarkers[2] < i && i < blockMarkers[3]);
|
||||||
|
|
||||||
|
const exportFile = `module.exports = ${fileFunction.join('\n').slice(17)}`;
|
||||||
|
const exportTest = [
|
||||||
|
`const test = require('tape');`,
|
||||||
|
`const ${fileName} = require('./${fileName}.js');`,
|
||||||
|
`test('Testing ${fileName}', (t) => {`,
|
||||||
|
`//For more information on all the methods supported by tape\n//Please go to https://github.com/substack/tape`,
|
||||||
|
`//t.deepEqual(${fileName}(args..), 'Expected');`,
|
||||||
|
`//t.equal(${fileName}(args..), 'Expected');`,
|
||||||
|
`//t.false(${fileName}(args..), 'Expected');`,
|
||||||
|
`//t.true(${fileName}(args..), 'Expected');`,
|
||||||
|
`//t.throws(${fileName}(args..), 'Expected');`,
|
||||||
|
`t.end();`,
|
||||||
|
`});`
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
fs.writeFileSync(`${TEST_PATH}/${fileName}/${fileName}.js`, exportFile);
|
||||||
|
fs.writeFileSync(`${TEST_PATH}/${fileName}/${fileName}.test.js`, exportTest);
|
||||||
|
|
||||||
|
return fileName;
|
||||||
|
});
|
||||||
@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
This is the web builder script that generates the README file.
|
|
||||||
Run using `npm run webber`.
|
|
||||||
*/
|
|
||||||
// Load modules
|
|
||||||
const fs = require('fs-extra'), path = require('path'), chalk = require('chalk'),
|
|
||||||
md = require('markdown-it')(), minify = require('html-minifier').minify;
|
|
||||||
// Compile the mini.css framework and custom CSS styles, using `node-sass`.
|
|
||||||
const sass = require('node-sass');
|
|
||||||
sass.render({
|
|
||||||
file: path.join('docs','mini','flavor.scss'),
|
|
||||||
outFile: path.join('docs','mini.css'),
|
|
||||||
outputStyle: 'compressed'
|
|
||||||
}, function(err, result) {
|
|
||||||
if(!err){
|
|
||||||
fs.writeFile(path.join('docs','mini.css'), result.css, function(err2){
|
|
||||||
if(!err2) console.log(`${chalk.green('SUCCESS!')} mini.css file generated!`);
|
|
||||||
else console.log(`${chalk.red('ERROR!')} During mini.css file generation: ${err}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log(`${chalk.red('ERROR!')} During mini.css file generation: ${err}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Set variables for paths
|
|
||||||
const snippetsPath = './snippets', staticPartsPath = './static-parts', docsPath = './docs';
|
|
||||||
// Set variables for script
|
|
||||||
let snippets = {}, startPart = '', endPart = '', output = '', tagDbData = {};
|
|
||||||
// Load helper functions (these are from existing snippets in 30 seconds of code!)
|
|
||||||
const objectFromPairs = arr => arr.reduce((a, v) => (a[v[0]] = v[1], a), {});
|
|
||||||
const capitalize = (str, lowerRest = false) => str.slice(0, 1).toUpperCase() + (lowerRest ? str.slice(1).toLowerCase() : str.slice(1));
|
|
||||||
// Start the timer of the script
|
|
||||||
console.time('Builder');
|
|
||||||
// Synchronously read all snippets and sort them as necessary (case-insensitive)
|
|
||||||
try {
|
|
||||||
let snippetFilenames = fs.readdirSync(snippetsPath);
|
|
||||||
snippetFilenames.sort((a, b) => {
|
|
||||||
a = a.toLowerCase();
|
|
||||||
b = b.toLowerCase();
|
|
||||||
if (a < b) return -1;
|
|
||||||
if (a > b) return 1;
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
// Store the data read from each snippet in the appropriate object
|
|
||||||
for(let snippet of snippetFilenames) snippets[snippet] = fs.readFileSync(path.join(snippetsPath,snippet),'utf8');
|
|
||||||
}
|
|
||||||
catch (err){ // Handle errors (hopefully not!)
|
|
||||||
console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
// Load static parts for the index.html file
|
|
||||||
try {
|
|
||||||
startPart = fs.readFileSync(path.join(staticPartsPath,'index-start.html'),'utf8');
|
|
||||||
endPart = fs.readFileSync(path.join(staticPartsPath,'index-end.html'),'utf8');
|
|
||||||
}
|
|
||||||
catch (err){ // Handle errors (hopefully not!)
|
|
||||||
console.log(`${chalk.red('ERROR!')} During static part loading: ${err}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
// Load tag data from the database
|
|
||||||
try {
|
|
||||||
tagDbData = objectFromPairs(fs.readFileSync('tag_database','utf8').split('\n').slice(0,-1).map(v => v.split(':').slice(0,2)));
|
|
||||||
}
|
|
||||||
catch (err){ // Handle errors (hopefully not!)
|
|
||||||
console.log(`${chalk.red('ERROR!')} During tag database loading: ${err}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
// Create the output for the index.html file
|
|
||||||
try {
|
|
||||||
// Add the start static part
|
|
||||||
output += `${startPart+'\n'}`;
|
|
||||||
let uncategorizedOutput = '';
|
|
||||||
// Loop over tags and snippets to create the table of contents
|
|
||||||
for(let tag of [...new Set(Object.entries(tagDbData).map(t => t[1]))].filter(v => v).sort((a,b) => a.localeCompare(b))){
|
|
||||||
if(capitalize(tag, true)=='Uncategorized') {
|
|
||||||
uncategorizedOutput +=`<h3>`+md.render(`${capitalize(tag, true)}\n`).replace(/<p>/g,'').replace(/<\/p>/g,'')+`</h3>`;
|
|
||||||
for(let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
|
||||||
uncategorizedOutput += md.render(`[${taggedSnippet[0]}](#${taggedSnippet[0].toLowerCase()})\n`).replace(/<p>/g,'').replace(/<\/p>/g,'').replace(/<a/g,'<a class="sublink-1"');
|
|
||||||
uncategorizedOutput += '\n';
|
|
||||||
} else {
|
|
||||||
output +=`<h3>`+md.render(`${capitalize(tag, true)}\n`).replace(/<p>/g,'').replace(/<\/p>/g,'')+`</h3>`;
|
|
||||||
for(let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
|
||||||
output += md.render(`[${taggedSnippet[0]}](#${taggedSnippet[0].toLowerCase()})\n`).replace(/<p>/g,'').replace(/<\/p>/g,'').replace(/<a/g,'<a class="sublink-1"');
|
|
||||||
output += '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output += uncategorizedOutput;
|
|
||||||
output += `</nav><main class="col-sm-12 col-md-8 col-lg-9" style="height: 100%;overflow-y: auto; background: #eceef2; padding: 0;">`;
|
|
||||||
output += `<a id="top"> </a>`;
|
|
||||||
uncategorizedOutput = '';
|
|
||||||
// Loop over tags and snippets to create the list of snippets
|
|
||||||
for(let tag of [...new Set(Object.entries(tagDbData).map(t => t[1]))].filter(v => v).sort((a,b) => a.localeCompare(b))){
|
|
||||||
if(capitalize(tag, true)=='Uncategorized') {
|
|
||||||
uncategorizedOutput +=md.render(`## ${capitalize(tag, true)}\n`).replace(/<h2>/g,'<h2 style="text-align:center;">');
|
|
||||||
for(let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
|
||||||
uncategorizedOutput += '<div class="card fluid"><div class="section double-padded">' + md.render(`\n${snippets[taggedSnippet[0]+'.md']}`).replace(/<h3/g,`<h3 id="${taggedSnippet[0].toLowerCase()}"`).replace(/<\/h3>/g,'</h3></div><div class="section double-padded">') + '</div></div><br/>';
|
|
||||||
} else {
|
|
||||||
output +=md.render(`## ${capitalize(tag, true)}\n`).replace(/<h2>/g,'<h2 style="text-align:center;">');
|
|
||||||
for(let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
|
||||||
output += '<div class="card fluid"><div class="section double-padded">' + md.render(`\n${snippets[taggedSnippet[0]+'.md']}`).replace(/<h3/g,`<h3 id="${taggedSnippet[0].toLowerCase()}"`).replace(/<\/h3>/g,'</h3></div><div class="section double-padded">') + '</div></div><br/>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output += uncategorizedOutput;
|
|
||||||
// Add the ending static part
|
|
||||||
output += `\n${endPart+'\n'}`;
|
|
||||||
// Minify output
|
|
||||||
output = minify(output, {
|
|
||||||
collapseBooleanAttributes: true,
|
|
||||||
collapseWhitespace: false,
|
|
||||||
decodeEntities: false,
|
|
||||||
minifyCSS: true,
|
|
||||||
minifyJS: true,
|
|
||||||
keepClosingSlash: true,
|
|
||||||
processConditionalComments: true,
|
|
||||||
removeAttributeQuotes: false,
|
|
||||||
removeComments: true,
|
|
||||||
removeEmptyAttributes: false,
|
|
||||||
removeOptionalTags: false,
|
|
||||||
removeScriptTypeAttributes: false,
|
|
||||||
removeStyleLinkTypeAttributes: false,
|
|
||||||
trimCustomFragments: true,
|
|
||||||
});
|
|
||||||
// Write to the index.html file
|
|
||||||
fs.writeFileSync(path.join(docsPath,'index.html'), output);
|
|
||||||
}
|
|
||||||
catch (err){ // Handle errors (hopefully not!)
|
|
||||||
console.log(`${chalk.red('ERROR!')} During index.html generation: ${err}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
// Log a success message
|
|
||||||
console.log(`${chalk.green('SUCCESS!')} index.html file generated!`);
|
|
||||||
// Log the time taken
|
|
||||||
console.timeEnd('Builder');
|
|
||||||
192
scripts/web.js
Normal file
192
scripts/web.js
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
This is the web builder script that generates the README file.
|
||||||
|
Run using `npm run webber`.
|
||||||
|
*/
|
||||||
|
// Load modules
|
||||||
|
const fs = require('fs-extra'),
|
||||||
|
path = require('path'),
|
||||||
|
chalk = require('chalk'),
|
||||||
|
md = require('markdown-it')(),
|
||||||
|
minify = require('html-minifier').minify;
|
||||||
|
// Compile the mini.css framework and custom CSS styles, using `node-sass`.
|
||||||
|
const sass = require('node-sass');
|
||||||
|
sass.render(
|
||||||
|
{
|
||||||
|
file: path.join('docs', 'mini', 'flavor.scss'),
|
||||||
|
outFile: path.join('docs', 'mini.css'),
|
||||||
|
outputStyle: 'compressed'
|
||||||
|
},
|
||||||
|
function(err, result) {
|
||||||
|
if (!err) {
|
||||||
|
fs.writeFile(path.join('docs', 'mini.css'), result.css, function(err2) {
|
||||||
|
if (!err2) console.log(`${chalk.green('SUCCESS!')} mini.css file generated!`);
|
||||||
|
else console.log(`${chalk.red('ERROR!')} During mini.css file generation: ${err}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(`${chalk.red('ERROR!')} During mini.css file generation: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// Set variables for paths
|
||||||
|
const snippetsPath = './snippets',
|
||||||
|
staticPartsPath = './static-parts',
|
||||||
|
docsPath = './docs';
|
||||||
|
// Set variables for script
|
||||||
|
let snippets = {},
|
||||||
|
startPart = '',
|
||||||
|
endPart = '',
|
||||||
|
output = '',
|
||||||
|
tagDbData = {};
|
||||||
|
// Load helper functions (these are from existing snippets in 30 seconds of code!)
|
||||||
|
const objectFromPairs = arr => arr.reduce((a, v) => ((a[v[0]] = v[1]), a), {});
|
||||||
|
const capitalize = (str, lowerRest = false) =>
|
||||||
|
str.slice(0, 1).toUpperCase() + (lowerRest ? str.slice(1).toLowerCase() : str.slice(1));
|
||||||
|
// Start the timer of the script
|
||||||
|
console.time('Builder');
|
||||||
|
// Synchronously read all snippets and sort them as necessary (case-insensitive)
|
||||||
|
try {
|
||||||
|
let snippetFilenames = fs.readdirSync(snippetsPath);
|
||||||
|
snippetFilenames.sort((a, b) => {
|
||||||
|
a = a.toLowerCase();
|
||||||
|
b = b.toLowerCase();
|
||||||
|
if (a < b) return -1;
|
||||||
|
if (a > b) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
// Store the data read from each snippet in the appropriate object
|
||||||
|
for (let snippet of snippetFilenames)
|
||||||
|
snippets[snippet] = fs.readFileSync(path.join(snippetsPath, snippet), 'utf8');
|
||||||
|
} catch (err) {
|
||||||
|
// Handle errors (hopefully not!)
|
||||||
|
console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
// Load static parts for the index.html file
|
||||||
|
try {
|
||||||
|
startPart = fs.readFileSync(path.join(staticPartsPath, 'index-start.html'), 'utf8');
|
||||||
|
endPart = fs.readFileSync(path.join(staticPartsPath, 'index-end.html'), 'utf8');
|
||||||
|
} catch (err) {
|
||||||
|
// Handle errors (hopefully not!)
|
||||||
|
console.log(`${chalk.red('ERROR!')} During static part loading: ${err}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
// Load tag data from the database
|
||||||
|
try {
|
||||||
|
tagDbData = objectFromPairs(
|
||||||
|
fs
|
||||||
|
.readFileSync('tag_database', 'utf8')
|
||||||
|
.split('\n')
|
||||||
|
.slice(0, -1)
|
||||||
|
.map(v => v.split(':').slice(0, 2))
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
// Handle errors (hopefully not!)
|
||||||
|
console.log(`${chalk.red('ERROR!')} During tag database loading: ${err}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
// Create the output for the index.html file
|
||||||
|
try {
|
||||||
|
// Add the start static part
|
||||||
|
output += `${startPart + '\n'}`;
|
||||||
|
let uncategorizedOutput = '';
|
||||||
|
// Loop over tags and snippets to create the table of contents
|
||||||
|
for (let tag of [...new Set(Object.entries(tagDbData).map(t => t[1]))]
|
||||||
|
.filter(v => v)
|
||||||
|
.sort((a, b) => a.localeCompare(b))) {
|
||||||
|
if (capitalize(tag, true) == 'Uncategorized') {
|
||||||
|
uncategorizedOutput +=
|
||||||
|
`<h3>` +
|
||||||
|
md
|
||||||
|
.render(`${capitalize(tag, true)}\n`)
|
||||||
|
.replace(/<p>/g, '')
|
||||||
|
.replace(/<\/p>/g, '') +
|
||||||
|
`</h3>`;
|
||||||
|
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
||||||
|
uncategorizedOutput += md
|
||||||
|
.render(`[${taggedSnippet[0]}](#${taggedSnippet[0].toLowerCase()})\n`)
|
||||||
|
.replace(/<p>/g, '')
|
||||||
|
.replace(/<\/p>/g, '')
|
||||||
|
.replace(/<a/g, '<a class="sublink-1"');
|
||||||
|
uncategorizedOutput += '\n';
|
||||||
|
} else {
|
||||||
|
output +=
|
||||||
|
`<h3>` +
|
||||||
|
md
|
||||||
|
.render(`${capitalize(tag, true)}\n`)
|
||||||
|
.replace(/<p>/g, '')
|
||||||
|
.replace(/<\/p>/g, '') +
|
||||||
|
`</h3>`;
|
||||||
|
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
||||||
|
output += md
|
||||||
|
.render(`[${taggedSnippet[0]}](#${taggedSnippet[0].toLowerCase()})\n`)
|
||||||
|
.replace(/<p>/g, '')
|
||||||
|
.replace(/<\/p>/g, '')
|
||||||
|
.replace(/<a/g, '<a class="sublink-1"');
|
||||||
|
output += '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output += uncategorizedOutput;
|
||||||
|
output += `</nav><main class="col-sm-12 col-md-8 col-lg-9" style="height: 100%;overflow-y: auto; background: #eceef2; padding: 0;">`;
|
||||||
|
output += `<a id="top"> </a>`;
|
||||||
|
uncategorizedOutput = '';
|
||||||
|
// Loop over tags and snippets to create the list of snippets
|
||||||
|
for (let tag of [...new Set(Object.entries(tagDbData).map(t => t[1]))]
|
||||||
|
.filter(v => v)
|
||||||
|
.sort((a, b) => a.localeCompare(b))) {
|
||||||
|
if (capitalize(tag, true) == 'Uncategorized') {
|
||||||
|
uncategorizedOutput += md
|
||||||
|
.render(`## ${capitalize(tag, true)}\n`)
|
||||||
|
.replace(/<h2>/g, '<h2 style="text-align:center;">');
|
||||||
|
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
||||||
|
uncategorizedOutput +=
|
||||||
|
'<div class="card fluid"><div class="section double-padded">' +
|
||||||
|
md
|
||||||
|
.render(`\n${snippets[taggedSnippet[0] + '.md']}`)
|
||||||
|
.replace(/<h3/g, `<h3 id="${taggedSnippet[0].toLowerCase()}"`)
|
||||||
|
.replace(/<\/h3>/g, '</h3></div><div class="section double-padded">') +
|
||||||
|
'</div></div><br/>';
|
||||||
|
} else {
|
||||||
|
output += md
|
||||||
|
.render(`## ${capitalize(tag, true)}\n`)
|
||||||
|
.replace(/<h2>/g, '<h2 style="text-align:center;">');
|
||||||
|
for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
|
||||||
|
output +=
|
||||||
|
'<div class="card fluid"><div class="section double-padded">' +
|
||||||
|
md
|
||||||
|
.render(`\n${snippets[taggedSnippet[0] + '.md']}`)
|
||||||
|
.replace(/<h3/g, `<h3 id="${taggedSnippet[0].toLowerCase()}"`)
|
||||||
|
.replace(/<\/h3>/g, '</h3></div><div class="section double-padded">') +
|
||||||
|
'</div></div><br/>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output += uncategorizedOutput;
|
||||||
|
// Add the ending static part
|
||||||
|
output += `\n${endPart + '\n'}`;
|
||||||
|
// Minify output
|
||||||
|
output = minify(output, {
|
||||||
|
collapseBooleanAttributes: true,
|
||||||
|
collapseWhitespace: true,
|
||||||
|
decodeEntities: false,
|
||||||
|
minifyCSS: true,
|
||||||
|
minifyJS: true,
|
||||||
|
keepClosingSlash: true,
|
||||||
|
processConditionalComments: true,
|
||||||
|
removeAttributeQuotes: false,
|
||||||
|
removeComments: true,
|
||||||
|
removeEmptyAttributes: false,
|
||||||
|
removeOptionalTags: false,
|
||||||
|
removeScriptTypeAttributes: false,
|
||||||
|
removeStyleLinkTypeAttributes: false,
|
||||||
|
trimCustomFragments: true
|
||||||
|
});
|
||||||
|
// Write to the index.html file
|
||||||
|
fs.writeFileSync(path.join(docsPath, 'index.html'), output);
|
||||||
|
} catch (err) {
|
||||||
|
// Handle errors (hopefully not!)
|
||||||
|
console.log(`${chalk.red('ERROR!')} During index.html generation: ${err}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
// Log a success message
|
||||||
|
console.log(`${chalk.green('SUCCESS!')} index.html file generated!`);
|
||||||
|
// Log the time taken
|
||||||
|
console.timeEnd('Builder');
|
||||||
@ -5,9 +5,13 @@ Returns the average of an of two or more numbers/arrays.
|
|||||||
Use `Array.reduce()` to add each value to an accumulator, initialized with a value of `0`, divide by the `length` of the array.
|
Use `Array.reduce()` to add each value to an accumulator, initialized with a value of `0`, divide by the `length` of the array.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const average = (...arr) => [].concat(...arr).reduce((acc, val) => acc + val, 0) / arr.length;
|
const average = (...arr) => {
|
||||||
|
const nums = [].concat(...arr);
|
||||||
|
return nums.reduce((acc, val) => acc + val, 0) / nums.length;
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
average([1, 2, 3]); // 2
|
average([1, 2, 3]); // 2
|
||||||
|
average(1, 2, 3); // 2
|
||||||
```
|
```
|
||||||
|
|||||||
@ -9,6 +9,6 @@ const byteSize = str => new Blob([str]).size;
|
|||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
byteSize("😀"); // 4
|
byteSize('😀'); // 4
|
||||||
byteSize("Hello World"); // 11
|
byteSize('Hello World'); // 11
|
||||||
```
|
```
|
||||||
|
|||||||
24
snippets/escapeHTML.md
Normal file
24
snippets/escapeHTML.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
### escapeHTML
|
||||||
|
|
||||||
|
Escapes a string for use in HTML.
|
||||||
|
|
||||||
|
Use `String.replace()` with a regex that matches the characters that need to be escaped, using a callback function to replace each character instance with its associated escaped character using a dictionary (object).
|
||||||
|
|
||||||
|
```js
|
||||||
|
const escapeHTML = str =>
|
||||||
|
str.replace(
|
||||||
|
/[&<>'"]/g,
|
||||||
|
tag =>
|
||||||
|
({
|
||||||
|
'&': '&',
|
||||||
|
'<': '<',
|
||||||
|
'>': '>',
|
||||||
|
"'": ''',
|
||||||
|
'"': '"'
|
||||||
|
}[tag] || tag)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
escapeHTML('<a href="#">Me & you</a>'); // '<a href="#">Me & you</a>'
|
||||||
|
```
|
||||||
@ -14,5 +14,5 @@ const fibonacci = n =>
|
|||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
fibonacci(6); // 720
|
fibonacci(6); // [0, 1, 1, 2, 3, 5]
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
### lcm
|
### lcm
|
||||||
|
|
||||||
Returns the least common multiple of two or numbers/arrays.
|
Returns the least common multiple of two or more numbers/arrays.
|
||||||
|
|
||||||
Use the greatest common divisor (GCD) formula and `Math.abs()` to determine the least common multiple.
|
Use the greatest common divisor (GCD) formula and `Math.abs()` to determine the least common multiple.
|
||||||
The GCD formula uses recursion.
|
The GCD formula uses recursion.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const lcm = (...arr) => {
|
const lcm = (...arr) => {
|
||||||
let data = [].concat(...arr);
|
|
||||||
const gcd = (x, y) => (!y ? x : gcd(y, x % y));
|
const gcd = (x, y) => (!y ? x : gcd(y, x % y));
|
||||||
const helperLcm = (x, y) => x * y / gcd(x, y);
|
const _lcm = (x, y) => x * y / gcd(x, y);
|
||||||
return arr.reduce((a, b) => helperLcm(a, b));
|
return [].concat(...arr).reduce((a, b) => _lcm(a, b));
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
19
snippets/lowercaseKeys.md
Normal file
19
snippets/lowercaseKeys.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
### lowercaseKeys
|
||||||
|
|
||||||
|
Creates a new object from the specified object, where all the keys are in lowercase.
|
||||||
|
|
||||||
|
Use `Object.keys()` and `Array.reduce()` to create a new object from the specified object.
|
||||||
|
Convert each key in the original object to lowercase, using `String.toLowerCase()`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const lowercaseKeys = obj =>
|
||||||
|
Object.keys(obj).reduce((acc, key) => {
|
||||||
|
acc[key.toLowerCase()] = obj[key];
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
const myObj = { Name: 'Adam', sUrnAME: 'Smith' };
|
||||||
|
const myObjLower = lowercaseKeys(myObj); // {name: 'Adam', surname: 'Smith'};
|
||||||
|
```
|
||||||
@ -5,38 +5,7 @@ Returns the maximum value out of two or more numbers/arrays.
|
|||||||
Use `Math.max()` combined with the spread operator (`...`) to get the maximum value in the array.
|
Use `Math.max()` combined with the spread operator (`...`) to get the maximum value in the array.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
const max = (...arr) => Math.max(...[].concat(...arr));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const max = (...arr) => Math.max(...[].concat(...arr);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|||||||
30
snippets/onUserInputChange.md
Normal file
30
snippets/onUserInputChange.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
### onUserInputChange
|
||||||
|
|
||||||
|
Run the callback whenever the user input type changes (`mouse` or `touch`). Useful for enabling/disabling code depending on the input device. This process is dynamic and works with hybrid devices (e.g. touchscreen laptops).
|
||||||
|
|
||||||
|
Use two event listeners. Assume `mouse` input initially and bind a `touchstart` event listener to the document.
|
||||||
|
On `touchstart`, add a `mousemove` event listener to listen for two consecutive `mousemove` events firing within 20ms, using `performance.now()`.
|
||||||
|
Run the callback with the input type as an argument in either of these situations.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const onUserInputChange = callback => {
|
||||||
|
let type = 'mouse',
|
||||||
|
lastTime = 0;
|
||||||
|
const mousemoveHandler = () => {
|
||||||
|
const now = performance.now();
|
||||||
|
if (now - lastTime < 20)
|
||||||
|
(type = 'mouse'), callback(type), document.removeEventListener('mousemove', mousemoveHandler);
|
||||||
|
lastTime = now;
|
||||||
|
};
|
||||||
|
document.addEventListener('touchstart', () => {
|
||||||
|
if (type === 'touch') return;
|
||||||
|
(type = 'touch'), callback(type), document.addEventListener('mousemove', mousemoveHandler);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
onUserInputChange(type => {
|
||||||
|
console.log('The user is now using', type, 'as an input method.');
|
||||||
|
});
|
||||||
|
```
|
||||||
13
snippets/splitLines.md
Normal file
13
snippets/splitLines.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
### splitLines
|
||||||
|
|
||||||
|
Splits a multiline string into an array of lines.
|
||||||
|
|
||||||
|
Use `String.split()` and a regular expression to match line breaks and create an array.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const splitLines = str => str.split(/\r?\n/);
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
splitLines('This\nis a\nmultiline\nstring.\n'); // ['This', 'is a', 'multiline', 'string' , '']
|
||||||
|
```
|
||||||
23
snippets/unescapeHTML.md
Normal file
23
snippets/unescapeHTML.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
### unescapeHTML
|
||||||
|
|
||||||
|
Unescapes escaped HTML characters.
|
||||||
|
|
||||||
|
Use `String.replace()` with a regex that matches the characters that need to be unescaped, using a callback function to replace each escaped character instance with its associated unescaped character using a dictionary (object).
|
||||||
|
|
||||||
|
```js
|
||||||
|
const unescapeHTML = str =>
|
||||||
|
str.replace(
|
||||||
|
/&|<|>|'|"/g,
|
||||||
|
tag =>
|
||||||
|
({
|
||||||
|
'&': '&',
|
||||||
|
'<': '<',
|
||||||
|
'>': '>',
|
||||||
|
''': "'",
|
||||||
|
'"': '"'
|
||||||
|
}[tag] || tag)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
```js
|
||||||
|
unescapeHTML('<a href="#">Me & you</a>'); // '<a href="#">Me & you</a>'
|
||||||
|
```
|
||||||
@ -2,6 +2,7 @@ anagrams:string
|
|||||||
arrayToHtmlList:browser
|
arrayToHtmlList:browser
|
||||||
average:math
|
average:math
|
||||||
bottomVisible:browser
|
bottomVisible:browser
|
||||||
|
byteSize:string
|
||||||
call:adapter
|
call:adapter
|
||||||
capitalize:string
|
capitalize:string
|
||||||
capitalizeEveryWord:string
|
capitalizeEveryWord:string
|
||||||
@ -29,6 +30,7 @@ distinctValuesOfArray:array
|
|||||||
dropElements:array
|
dropElements:array
|
||||||
dropRight:array
|
dropRight:array
|
||||||
elementIsVisibleInViewport:browser
|
elementIsVisibleInViewport:browser
|
||||||
|
escapeHTML:string
|
||||||
escapeRegExp:string
|
escapeRegExp:string
|
||||||
everyNth:array
|
everyNth:array
|
||||||
extendHex:utility
|
extendHex:utility
|
||||||
@ -75,6 +77,7 @@ JSONToDate:date
|
|||||||
JSONToFile:node
|
JSONToFile:node
|
||||||
last:array
|
last:array
|
||||||
lcm:math
|
lcm:math
|
||||||
|
lowercaseKeys:object
|
||||||
mapObject:array
|
mapObject:array
|
||||||
max:math
|
max:math
|
||||||
median:math
|
median:math
|
||||||
@ -83,8 +86,9 @@ negate:logic
|
|||||||
nthElement:array
|
nthElement:array
|
||||||
objectFromPairs:object
|
objectFromPairs:object
|
||||||
objectToPairs:object
|
objectToPairs:object
|
||||||
|
onUserInputChange:browser
|
||||||
orderBy:object
|
orderBy:object
|
||||||
palindrome:math
|
palindrome:string
|
||||||
percentile:math
|
percentile:math
|
||||||
pick:array
|
pick:array
|
||||||
pipeFunctions:adapter
|
pipeFunctions:adapter
|
||||||
@ -118,6 +122,7 @@ similarity:array
|
|||||||
sleep:function
|
sleep:function
|
||||||
sortCharactersInString:string
|
sortCharactersInString:string
|
||||||
speechSynthesis:media
|
speechSynthesis:media
|
||||||
|
splitLines:string
|
||||||
spreadOver:adapter
|
spreadOver:adapter
|
||||||
standardDeviation:math
|
standardDeviation:math
|
||||||
sum:math
|
sum:math
|
||||||
@ -136,6 +141,7 @@ toOrdinalSuffix:utility
|
|||||||
toSnakeCase:string
|
toSnakeCase:string
|
||||||
truncateString:string
|
truncateString:string
|
||||||
truthCheckCollection:object
|
truthCheckCollection:object
|
||||||
|
unescapeHTML:string
|
||||||
union:array
|
union:array
|
||||||
UUIDGeneratorBrowser:browser
|
UUIDGeneratorBrowser:browser
|
||||||
UUIDGeneratorNode:node
|
UUIDGeneratorNode:node
|
||||||
|
|||||||
Reference in New Issue
Block a user