import * as perisopic from 'periscopic'; import { handle } from './handlers.js'; import { encode } from '@jridgewell/sourcemap-codec'; /** @type {(str?: string) => string} str */ let btoa = () => { throw new Error( 'Unsupported environment: `window.btoa` or `Buffer` should be supported.' ); }; if (typeof window !== 'undefined' && typeof window.btoa === 'function') { btoa = (str) => window.btoa(unescape(encodeURIComponent(str))); } else if (typeof Buffer === 'function') { btoa = (str) => Buffer.from(str, 'utf-8').toString('base64'); } /** @typedef {import('estree').Node} Node */ /** * @typedef {{ * file?: string; * sourceMapSource?: string; * sourceMapContent?: string; * sourceMapEncodeMappings?: boolean; // default true * getName?: (name: string) => string; * }} PrintOptions */ /** * @param {Node} node * @param {PrintOptions} opts * @returns {{ code: string, map: any }} // TODO */ export function print(node, opts = {}) { if (Array.isArray(node)) { return print( { type: 'Program', body: node, sourceType: 'module' }, opts ); } const { getName = /** @param {string} x */ (x) => { throw new Error(`Unhandled sigil @${x}`); } } = opts; let { map: scope_map, scope } = perisopic.analyze(node); const deconflicted = new WeakMap(); const chunks = handle(node, { indent: '', getName, scope, scope_map, deconflicted, comments: [] }); /** @typedef {[number, number, number, number]} Segment */ let code = ''; let current_column = 0; /** @type {Segment[][]} */ let mappings = []; /** @type {Segment[]} */ let current_line = []; for (let i = 0; i < chunks.length; i += 1) { const chunk = chunks[i]; code += chunk.content; if (chunk.loc) { current_line.push([ current_column, 0, // source index is always zero chunk.loc.start.line - 1, chunk.loc.start.column ]); } for (let i = 0; i < chunk.content.length; i += 1) { if (chunk.content[i] === '\n') { mappings.push(current_line); current_line = []; current_column = 0; } else { current_column += 1; } } if (chunk.loc) { current_line.push([ current_column, 0, // source index is always zero chunk.loc.end.line - 1, chunk.loc.end.column ]); } } mappings.push(current_line); const map = { version: 3, /** @type {string[]} */ names: [], sources: [opts.sourceMapSource || null], sourcesContent: [opts.sourceMapContent || null], mappings: opts.sourceMapEncodeMappings == undefined || opts.sourceMapEncodeMappings ? encode(mappings) : mappings }; Object.defineProperties(map, { toString: { enumerable: false, value: function toString() { return JSON.stringify(this); } }, toUrl: { enumerable: false, value: function toUrl() { return ( 'data:application/json;charset=utf-8;base64,' + btoa(this.toString()) ); } } }); return { code, map }; }