UNPKG

fuse-box

Version:

Fuse-Box a bundler that does it right

390 lines (341 loc) 10.9 kB
import * as path from "path"; import * as fs from "fs"; import * as fsExtra from "fs-extra"; import { utils } from "realm-utils"; import { Config } from "./Config"; import * as LegoAPI from "lego-api"; const userFuseDir = Config.PROJECT_ROOT; const stylesheetExtensions = new Set<string>([".css", ".sass", ".scss", ".styl", ".less"]); const MBLACKLIST = [ "freelist", "sys", ]; export type Concat = { add(fileName: string | null, content: string | Buffer, sourceMap?: string): void; content: Buffer; sourceMap: string; }; export type ConcatModule = { new(generateSourceMap: boolean, outputFileName: string, seperator: string): Concat; }; export const Concat: ConcatModule = require("concat-with-sourcemaps"); export function contains(array: any[], obj: any) { return array && array.indexOf(obj) > -1; } export function replaceAliasRequireStatement(requireStatement: string, aliasName: string, aliasReplacement: string) { requireStatement = requireStatement.replace(aliasName, aliasReplacement); requireStatement = path.normalize(requireStatement); return requireStatement; } // export function legoApi(fname: string, conditions: any) { // const contents = fs.readFileSync(fname).toString(); // const lines = contents.split(/\r?\n/); // let result = []; // let consume = true; // lines.forEach(line => { // const condition = line.match(/^\s*\/\*\s*@if\s([\w]+)+\s*\*\//) // const endCondition = line.match(/^\s*\/\*\s*@end\s*\*\//); // if (condition || endCondition) { // if (condition) { // const variableName = condition[1]; // if (!conditions[variableName]) { // consume = false; // } // } // if (endCondition) { // consume = true; // } // } else { // if (consume) { // if (!/^\s+$/.test(line) && line) { // result.push(line); // } // } // } // }); // return result.join("\n"); // } export function jsCommentTemplate(fname: string, conditions: any, variables: any, raw: any) { const contents = fs.readFileSync(fname).toString(); let data = LegoAPI.parse(contents).render(conditions); for (let varName in variables) { data = data.replace(`$${varName}$`, JSON.stringify(variables[varName])); } for (let varName in raw) { data = data.replace(`$${varName}$`, raw[varName]); } return data; } export function uglify(contents: string | Buffer, opts: any = {}) { const UglifyJs = require("uglify-js"); return UglifyJs.minify(contents.toString(), opts); } export function readFuseBoxModule(target: string) { return fs.readFileSync(path.join(Config.FUSEBOX_MODULES, target)).toString(); } export function write(fileName: string, contents: any) { return new Promise((resolve, reject) => { fs.writeFile(fileName, contents, (e) => { if (e) { return reject(e); } return resolve(); }); }); } export function camelCase(str: string) { let DEFAULT_REGEX = /[-_]+(.)?/g; function toUpper(match, group1) { return group1 ? group1.toUpperCase() : ""; } return str.replace(DEFAULT_REGEX, toUpper); } export function parseQuery(qstr) { let query = new Map<string, string>(); let a = qstr.split("&"); for (let i = 0; i < a.length; i++) { let b = a[i].split("="); query.set(decodeURIComponent(b[0]), decodeURIComponent(b[1] || "")); } return query; } /** * Does two things: * - If a relative path is given, * it is assumed to be relative to appRoot and is then made absolute * - Ensures that the directory containing the userPath exits (creates it if needed) */ export function ensureUserPath(userPath: string) { if (!path.isAbsolute(userPath)) { userPath = path.join(userFuseDir, userPath); } userPath = path.normalize(userPath); let dir = path.dirname(userPath); fsExtra.ensureDirSync(dir); return userPath; } export function ensureAbsolutePath(userPath: string) { if (!path.isAbsolute(userPath)) { return path.join(userFuseDir, userPath); } return userPath; } export function joinFuseBoxPath(...any): string { return ensureFuseBoxPath(path.join.apply(path, arguments)); } export function ensureDir(userPath: string) { if (!path.isAbsolute(userPath)) { userPath = path.join(userFuseDir, userPath); } userPath = path.normalize(userPath); fsExtra.ensureDirSync(userPath); return userPath; } export function isStylesheetExtension(str: string) { let ext = path.extname(str); return stylesheetExtensions.has(ext); } export function string2RegExp(obj: any) { let escapedRegEx = obj .replace(/\*/g, "@") .replace(/[.?*+[\]-]/g, "\\$&") .replace(/@@/g, ".*", "i") .replace(/@/g, "\\w{1,}", "i"); if (escapedRegEx.indexOf("$") === -1) { escapedRegEx += "$"; } return new RegExp(escapedRegEx); } export function removeFolder(userPath) { fsExtra.removeSync(userPath); } export function replaceExt(npath, ext): string { if (typeof npath !== "string") { return npath; } if (npath.length === 0) { return npath; } if (/\.[a-z0-9]+$/i.test(npath)) { return npath.replace(/\.[a-z0-9]+$/i, ext); } else { return npath + ext; } } export function isGlob(str: string): Boolean { if (!str) { return false; } return /\*/.test(str); } export function hashString(text: string) { var hash = 5381, index = text.length; while (index) { hash = (hash * 33) ^ text.charCodeAt(--index); } let data = hash >>> 0; return data.toString(16); } export function fastHash(text: string) { let hash = 0; if (text.length == 0) return hash; for (let i = 0; i < text.length; i++) { let char = text.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return hash.toString(16); } export function extractExtension(str: string) { const result = str.match(/\.([a-z0-9]+)\$?$/); if (!result) { throw new Error(`Can't extract extension from string ${str}`); } return result[1]; } export function ensureFuseBoxPath(input: string) { return input.replace(/\\/g, "/"); } export function transpileToEs5(contents: string) { const ts = require("typescript"); let tsconfg: any = { compilerOptions: { module: "commonjs", target: "es5" }, };; let result = ts.transpileModule(contents, tsconfg); return result.outputText; } export function ensurePublicExtension(url: string) { let ext = path.extname(url); if (ext === ".ts") { url = replaceExt(url, ".js"); } if (ext === ".tsx") { url = replaceExt(url, ".jsx"); } return url; } export function getBuiltInNodeModules(): Array<string> { const process: any = global.process; return Object.keys(process.binding("natives")).filter(m => { return !/^_|^internal|\//.test(m) && MBLACKLIST.indexOf(m) === -1; }); } export function findFileBackwards(target: string, limitPath: string): string { let [found, reachedLimit] = [false, false]; let filename = path.basename(target); let current = path.dirname(target); let iterations = 0; const maxIterations = 10; while (found === false && reachedLimit === false) { let targetFilePath = path.join(current, filename); if (fs.existsSync(targetFilePath)) { return targetFilePath; } if (limitPath === current) { reachedLimit = true; } // going backwards current = path.join(current, ".."); // Making sure we won't have any perpetual loops here iterations++; if (iterations > maxIterations) { reachedLimit = true; } } } export function walk(dir, options?: any) { var defaults = { recursive: false, }; options = Object.assign(defaults, options); var results = []; var list = fs.readdirSync(dir); list.forEach(function (file) { file = dir + "/" + file; var stat = fs.statSync(file); if (options.recursive) { if (stat && stat.isDirectory()) results = results.concat(walk(file)); else results.push(file); } else if (stat && stat.isFile()) { results.push(file); } }); return results; } /* UTILITIES */ export function filter(items: any, fn: any) { if (Array.isArray(items)) { return items.filter(fn); } if (utils.isPlainObject(items)) { let newObject = {}; for (let key in items) { if (items.hasOwnProperty(key)) { if (fn(items[key], key)) { newObject[key] = items[key] } } } return newObject; } } // converted and optimized from https://www.npmjs.com/package/cli-spinner const readline = require('readline'); class Spinner { public text: string = ''; public title: string = ''; public chars: string = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"; public stream: any = process.stdout; public id: number | any; public delay: number = 60; constructor(options: any) { if (typeof options === "string") { options = { text: options }; } else if (!options) { options = {}; } if (options.text) this.text = options.text; if (options.onTick) this.onTick = options.onTick; if (options.stream) this.stream = options.stream; if (options.title) this.title = options.title; if (options.delay) this.delay = options.delay; } public start() { let current = 0 this.id = setInterval(() => { let msg = this.chars[current] + ' ' + this.text if (this.text.includes('%s')) { msg = this.text.replace('%s', this.chars[current]) } this.onTick(msg) current = ++current % this.chars.length }, this.delay) return this } public stop(clear: boolean) { clearInterval(this.id) this.id = undefined if (clear) { this.clearLine(this.stream) } return this } public isSpinning(): boolean { return this.id !== undefined } public onTick(msg: any) { this.clearLine(this.stream) this.stream.write(msg) return this } public clearLine(stream: any) { readline.clearLine(stream, 0) readline.cursorTo(stream, 0) return this } } export { Spinner };