UNPKG

preval

Version:

Wannabe compiler tool for JS

185 lines (164 loc) 6.49 kB
import path from 'path'; import Prettier from 'prettier'; export const RED = '\x1b[31;1m'; export const GREEN = '\x1b[32m'; export const YELLOW = '\x1b[33m'; export const BLUE = '\x1b[34;1m'; export const WHITE = '\x1b[37m'; export const BOLD = '\x1b[;1;1m'; export const RESET = '\x1b[0m'; export const WHITE_BLACK = '\x1b[30;47m'; export const ORANGE = '\x1b[38;5;214m'; export const GOOD = '\x1b[40m 🙂 \x1b[0m'; export const BAD = '\x1b[41m 👎 \x1b[0m'; export function ASSERT(a, desc = '', ...rest) { if (!a) { console.log('ASSERT failed: ' + desc); console.log('rest:', rest); throw new Error('ASSERT failed: ' + desc + '; ' + JSON.stringify(rest)); } } export function fromMarkdownCase(md, fname) { // A test case either starts with `//` (new test case) or with `#` (existing test case). Reject the rest. // Returns a test case: {md, fname, head, fin} if (md[0] === '/' && md[1] === '/' && md[2] === ' ') { console.log('Converting new case in', fname, 'to an actual test case'); // Assume new test case return { fname, md, mdHead: '# Preval test case\n\n# ' + path.basename(fname) + '\n\n' + '> ' + fname .slice(fname.indexOf('tests/cases/') + 'tests/cases/'.length, -3) .split('/') .join(' > ') + '\n' + '>\n' + '> ' + md.slice(3, md.indexOf('\n')), mdChunks: ['## Input\n\n`````js filename=intro\n' + md.slice(md.indexOf('\n')).trim() + '\n`````\n'], fin: { intro: md.slice(md.indexOf('\n')), }, }; } else if (md[0] === '#') { const [mdHead, ...chunks] = md.split('\n## '); const mdInput = chunks.filter((s) => s.startsWith('Input\n'))[0]; ASSERT(mdInput, 'all test cases should have an input block', fname); const testCase = { md, fname, mdHead: mdHead.trim(), mdChunks: chunks.filter((s) => !s.startsWith('Output\n')).map((s) => '## ' + s.trim()), fin: {}, }; const fin = mdInput.replace(/\n`````js(?:[ \t]+filename=(.*?))\n([\s\S]*?)\n`````\n/, (_, name = intro, code) => { const n = name.trim(); if (testCase.fin[n]) throw new Error('Duplicate name: ' + n); testCase.fin[n] = code.trim(); }); if (!fin?.length) throw new Error('Invalid test case'); return testCase; } else { throw new Error('Invalid test case? Does not start with `//` nor `#`'); } } function fmat(code) { try { return Prettier.format(code, { parser: 'babel' }); } catch (e) { // Prettier error implies invalid transformation. Uups. throw new Error('Prettier error. Implies the resulting transform is invalid.\n' + e); } } export function toMarkdownCase({ md, mdHead, mdChunks, fname, fin, output }) { const content = mdHead + '\n\n' + mdChunks.join('\n\n') + '\n\n## Output\n\n' + Object.keys(output.files) .sort((a, b) => (a === 'intro' ? -1 : b === 'intro' ? 1 : a < b ? -1 : a > b ? 1 : 0)) .map((key) => '`````js filename=' + key + '\n' + fmat(output.files[key]).trim() + '\n`````') .join('\n\n'); return content; } async function question(msg) { // Probably massive overkill :/ let resolve, p = new Promise((ok) => (resolve = ok)); let rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); rl.question(msg + ' ', (answer) => resolve(answer)); let r = await p; rl.close(); return r; } async function yn(msg = 'Answer?') { // Keep asking the question in console until the user types `y`, `yes`, `n`, or `no`. let answer = String(await question(msg)).toLowerCase(); answer = String(answer).toLowerCase(); if (answer === 'n' || answer === 'no' || answer === 'q') return false; while (answer !== 'y' && answer !== 'yes') { answer = await question(msg + ': [y, n]'); answer = String(answer).toLowerCase(); if (answer === 'n' || answer === 'no' || answer === 'q') return false; } return true; } /** // hack to get package version at certain day blabla var names = [ 'gatsby', 'gatsby-image', 'gatsby-link', 'gatsby-plugin-google-tagmanager', 'gatsby-plugin-i18next', 'gatsby-plugin-react-helmet', //'gatsby-plugin-react-svg', //'gatsby-plugin-resolve-src', //'gatsby-plugin-robots-txt', 'gatsby-plugin-sharp', 'gatsby-plugin-styled-components', 'gatsby-react-router-scroll', 'gatsby-source-contentful', 'gatsby-source-filesystem', 'gatsby-transformer-remark', 'gatsby-transformer-sharp', ]; //names.forEach(name => { // only need to do this once unless you need to refresh. it just stores the remote fetch locally. //require('https').get('https://registry.npmjs.org/' + name, res => { res.setEncoding('utf8'); let body = ''; res.on('data', d => body += d); res.on('end', () => console.log('finished:', name, require('fs').writeFileSync('npm.' + name + '.json', body, 'utf8')))}) //}) var maps = new Map var init = new Map names.forEach(name => { var json = JSON.parse(require('fs').readFileSync('npm.' + name + '.json')).time; var older = 'latest' for (const key in json) { if (!/^[\d.]+$/.test(key)) delete json[key]; // prune anything that's not a regular publish else if (json[key] < '2020-10-15') older = key, delete json[key]; // prune older than target contentful ver pubdate. remember the last one before target date } var map = new Map; for (const key in json) map.set(json[key].slice(0, 10), key); maps.set(name, map) init.set(name, older); // initial version (as per prior to target date) }) init // start at date of target version and walk forward to today. if any version changed on any day then copy the version and stamp it as a set // https://github.com/gatsbyjs/gatsby/commit/2494ae111f56e412071b103f202124c71c309e9b#diff-01f6ae9ad71849950b24e4800c23defcd1440600c57d6cabbf2a4cac7bfbe8d0 (oct 15th) var sets = new Map; var today = new Date; var day = new Date('2020-10-15'); while (day <= today) { var c = false; maps.forEach((vers, name) => { if (vers.has(day.toISOString().slice(0,10))) c =init.set(name, vers.get(day.toISOString().slice(0,10))) && (c || name === 'gatsby-source-contentful'); }) if (c) sets.set(day.toISOString().slice(0,10), new Map(init)) day.setDate(day.getDate() + 1) } var arr = [] sets.forEach((map, day) => arr.push('// ' + day + '\nyarn --ignore-engines add ' + [...map.entries()].map(([name, ver]) => name + '@' + ver).join(' ')) ) console.log(arr.join('\n\n')) */