Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

hottie

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hottie - npm Package Compare versions

Comparing version
0.0.0
to
0.0.1
+37
bin.js
#!/usr/bin/env node
const sade = require('sade');
const command = require('./commands');
const { version } = require('./package');
sade('hottie')
.version(version)
.command('build [src]')
.describe('Build production assets')
.option('--minify', 'Minify production assets', true)
.option('-o, --dest', 'Path to output directory', 'build')
.action(command.build)
// .command('export [src]')
// .describe('Export pre-rendered pages')
// .option('-o, --dest', 'Path to output directory', 'build')
// .option('-w, --wait', 'Time (ms) to wait before scraping each route', 0)
// .option('-r, --routes', 'Comma-delimited list of routes to export')
// .option('-i, --insecure', 'Launch Chrome Headless without sandbox')
// .action((src, opts) => {
// opts.export = true;
// build(src, opts);
// })
.command('watch [src]')
.describe('Start development server')
.option('-H, --host', 'A hostname on which to start the application', 'localhost')
.option('-p, --port', 'A port number on which to start the application', 8080)
.option('-q, --quiet', 'Disable logging to terminal, including errors and warnings')
.option('--https', 'Run the application over HTTP/2 with HTTPS')
.option('--key', 'Path to custom SSL certificate key')
.option('--cert', 'Path to custom SSL certificate')
.option('--cacert', 'Path to custom CA certificate override')
.action(command.watch)
.parse(process.argv);
!function(){let e=new EventSource("/_hmr");e.addEventListener("hmr:start",(function(e){console.log("[hottie] Connected")})),e.addEventListener("hmr:update",(function(e){let t=JSON.parse(e.data);return console.log("[TODO]",t),location.reload()})),e.addEventListener("hmr:error",(function(e){console.error("[hottie]",e.data)}))}();
var fs = require('fs');
var util = require('util');
var path = require('path');
var tlist = require('totalist');
var colors = require('kleur');
var laccess = require('local-access');
const read = util.promisify(fs.readFile);
const write = util.promisify(fs.writeFile);
function exists(file, dir = '.') {
let str = path.resolve(dir, file);
return fs.existsSync(str) && str;
}
function absolute(target, root) {
return path.join(path.dirname(root), target);
}
function parse(str, opts = {}) {
return require('node-html-parser').parse(str, opts);
}
function toScript(tag) {
let { src, type } = tag.rawAttributes;
if (!src || /^(https?:)?\/\//.test(src)) return;
let input = src.startsWith('/') ? src.substring(1) : src;
let nomodule = 'nomodule' in tag.rawAttributes || type !== 'module';
return { input, nomodule, original: tag.toString() };
}
async function entries(template) {
const document = parse(await read(template, 'utf8'));
return document.querySelectorAll('script').map(toScript).filter(Boolean);
}
function mutate(src, opts) {
opts.dir = opts.dest || 'build';
opts.cwd = path.resolve(opts.cwd || '.');
opts.src = path.join(opts.cwd, src || 'src');
opts.dest = path.join(opts.cwd, opts.dir);
opts.template = opts.template || 'index.html';
opts.production = !!opts.production;
delete opts._; // bye
}
// modified pwa/core util
function merge(old, nxt, args) {
for (let k in nxt) {
if (k === 'rollup') continue;
if (typeof nxt[k] === 'function') {
old[k] = old[k] || {};
nxt[k](old[k], args);
} else {
old[k] = nxt[k] || old[k];
}
}
}
// TODO: lukeed/premove?
function remove(dir, cwd) {
if (cwd) dir = path.join(cwd, dir);
let stat = fs.lstatSync(dir);
if (stat.isDirectory()) {
fs.readdirSync(dir).forEach(str => remove(str, dir));
fs.rmdirSync(dir); // now empty
} else {
fs.unlinkSync(dir);
}
}
const defaults = {
publicPath: '/',
assets: {
test: /\.(svg|woff2?|ttf|eot|jpe?g|png|gif|mp4|mov|ogg|webm)$/,
},
resolve: {
extensions: ['.mjs', '.js', '.jsx', '.json'],
mainFields: ['browser', 'module', 'jsnext', 'main'],
},
commonjs: {
extensions: ['.js', '.cjs']
},
json: {
preferConst: true,
namedExports: true,
indent: ' ',
},
terser: {
mangle: true,
compress: true,
output: {
comments: false
}
}
};
function toAssets (argv, options={}) {
// TODO: mimetype dict?
const { test } = options;
const minify = !!argv.minify;
const isProd = argv.production;
const Manifest = new Map();
const Plugin = {
name: 'hottie:assets',
resolveId(file, importer) {
if (!test.test(file)) return;
let dir = path.dirname(importer);
return path.resolve(dir, file);
},
async load(file) {
if (!test.test(file)) return;
// TODO: base64 inline w/ limit?
const ref = this.emitFile({
type: 'asset',
source: await read(file),
name: path.basename(file),
});
return `export default import.meta.ROLLUP_FILE_URL_${ref};`;
}
};
if (isProd) {
Plugin.resolveFileUrl = function (info) {
// [absolute path]: [relative outpath]
Manifest.set(info.moduleId, info.fileName);
return null;
};
Plugin.renderChunk = function (_code, info) {
// [absolute path]: [relative outpath]
Manifest.set(info.facadeModuleId, info.fileName);
return null;
};
}
return { Manifest, Plugin };
}
function Config (argv, extra = {}) {
const { src, dest, production } = argv;
const { local, ...args } = extra;
const isProd = !!production;
Object.assign(args, argv);
let customize, options = { ...defaults };
if (local) {
let file = require(local);
merge(options, file, args);
customize = file.rollup;
}
const Assets = toAssets(argv, options.assets);
/**
* @type import('rollup').RollupOptions
*/
const config = {
input: extra.entry.input,
output: {
dir: dest,
sourcemap: !isProd,
minifyInternalExports: isProd,
entryFileNames: isProd ? '[name].[hash].js' : '[name].js',
assetFileNames: isProd ? '[name].[hash].[ext]' : '[name].[ext]',
chunkFileNames: isProd ? '[name].[hash].js' : '[name].js',
},
preserveModules: !isProd,
treeshake: isProd && {
moduleSideEffects: 'no-external',
tryCatchDeoptimization: false
},
plugins: [
Assets.Plugin,
require('@rollup/plugin-node-resolve').nodeResolve({
...options.resolve,
rootDir: src,
}),
require('@rollup/plugin-commonjs')({
...options.commonjs
}),
require('@rollup/plugin-json')({
compact: isProd,
...options.json
}),
]
};
if (customize) {
customize(config, args);
}
return {
Assets,
options,
config,
};
}
/**
* Crawl for & Copy HTML files
*/
async function HTML (argv, opts) {
const { src, dest, minify } = argv;
const { MANIFEST, publicPath } = opts;
// Find, modify, write HTML files
await tlist(src, async (rel, abs) => {
if (!/\.html?$/.test(rel)) return;
let target = path.join(dest, rel);
let content = await read(abs, 'utf8');
let document = parse(content);
// TODO: meta tags
document.querySelectorAll('script,link').forEach(tag => {
let { src, href } = tag.rawAttributes;
if (/^(https?:)?\/\//.test(href || src)) return;
let file = absolute(href || src, abs);
let entry = MANIFEST.get(file);
if (entry) {
tag.setAttribute(href ? 'href' : 'src', publicPath + entry);
}
});
// TODO: html-minifier ?
if (minify) document.removeWhitespace();
await write(target, document.toString());
});
}
async function index (src, argv={}) {
let prev = argv.production;
argv.production = prev == null || !!prev;
mutate(src, argv);
const template = exists(argv.template, argv.src);
if (!template) throw new Error(`Missing template: "${argv.template}"`);
if (exists(argv.dest)) {
console.log('removing existing "%s" directory', argv.dir);
remove(argv.dest);
}
let publicPath, MANIFEST = new Map;
const { rollup } = require('rollup');
const { terser } = require('rollup-plugin-terser');
const local = exists('hottie.config.js', argv.cwd);
if (local) console.log('loading custom configuration');
// TODO: if module only, generate nomodule too
const scripts = await entries(template);
for (let entry of scripts) {
let { Assets, config, options } = Config(argv, { local, entry });
publicPath = publicPath || options.publicPath;
let { output, ...rest } = config;
// Prod-only changes
if (argv.minify) rest.plugins.push(
terser(options.terser)
);
let now = Date.now();
let b = await rollup(rest);
// TODO: generate sibling nomodule here?
await Promise.all([].concat(output).map(b.write));
console.log('~> %s (%s)', entry.input, (Date.now() - now) + 'ms');
for (let [abs, rel] of Assets.Manifest) {
if (MANIFEST.has(abs)) console.log('Manifest has "%s" file!', abs);
else MANIFEST.set(abs, rel);
}
}
await HTML(argv, { MANIFEST, publicPath });
console.log('build complete~!');
}
/**
* Crawl for & Copy HTML files
*/
function HTML$1 (opts={}) {
const { src } = opts;
let script;
async function copy(ctx, file) {
let document = parse(await read(file, 'utf8'));
if (!script) {
// TODO: inject <script src="/@npm/hottie/client">
let data = await read('./client/index.js', 'utf8');
script = parse(`<script>${data}</script>\n`, { script: true });
}
document.querySelector('body').appendChild(script);
ctx.emitFile({
type: 'asset',
source: document.toString(),
fileName: path.relative(src, file)
});
}
return {
name: 'hottie:dev:html',
async buildStart() {
await tlist(src, async (rel, abs) => {
if (/\.html$/.test(rel)) {
this.addWatchFile(abs);
await copy(this, abs);
}
});
},
async watchChange(file) {
if (/\.html$/.test(file)) {
await copy(this, file);
}
}
};
}
/**
* Create a Watcher from base config
* @param config {import('rollup').RollupWatchOptions}
* @returns {import('rollup').RollupWatcher}
*/
function Watcher (config, opts={}) {
const { src, dest, onUpdate, onError } = opts;
const hasMap = !!config.output.sourcemap;
// dev-only plugins
config.plugins.push(
HTML$1({ src })
);
// Initialize Watcher
// Attach logging/event listeners
const watcher = require('rollup').watch(config);
let UPDATES = [];
let CHANGED = new Set;
watcher.on('change', file => {
if (file.startsWith(src)) {
CHANGED.add('/' + path.relative(src, file));
} else console.error('[CHANGE] NOT WITHIN SOURCE: "%s"', file);
});
watcher.on('event', evt => {
switch (evt.code) {
case 'START': {
UPDATES = [...CHANGED];
CHANGED.clear();
break;
}
case 'BUNDLE_END': {
// TODO: prettify
console.info(`Bundled in ${evt.duration}ms`);
if (onUpdate && UPDATES.length) onUpdate(UPDATES);
break;
}
case 'ERROR': {
console.error('ERROR', evt.error);
break;
}
}
});
return watcher;
}
// HTTPS
const { HOST, PORT } = process.env;
function Server (dir, opts={}) {
let https = false; // TODO
let port = PORT || opts.port;
let hostname = HOST || opts.host;
/** @type {import('http').Server} */
const server = require('hottie/server')(dir, opts);
return new Promise((res, rej) => {
server.listen(port, hostname, () => {
let { address } = server.address();
let isLocal = /^(::1?|localhost)$/.test(address);
hostname = isLocal ? 'localhost' : address;
const write = msg => process.stdout.write(msg + '\n');
const { local, network } = laccess({ port, hostname, https });
// lukeed/sirv
write('\n ' + colors.green('Your application is ready~! 🚀\n'));
write(' ' + colors.bold('- Local:') + ' ' + local);
isLocal || write(' ' + colors.bold('- Network:') + ' ' + network);
let border = '─'.repeat(Math.min(process.stdout.columns, 36) / 2);
write('\n' + border + colors.inverse(' LOGS ') + border + '\n');
return res(server);
});
});
}
async function index$1 (src, argv) {
mutate(src, argv);
const template = exists(argv.template, argv.src);
if (!template) throw new Error(`Missing template: "${argv.template}"`);
if (exists(argv.dest)) {
console.log('removing existing "%s" directory', argv.dir);
remove(argv.dest);
}
const local = exists('hottie.config.js', argv.cwd);
if (local) console.log('loading custom configuration');
const scripts = await entries(template);
// look for first ESM script
let entry = scripts.find(x => !x.nomodule);
if (!entry) console.log('TODO: APPEND SCRIPT TO TEMPLATE');
// Load & Assemble config
const { config } = Config(argv, { local, entry });
// TODO: import pwa-cli HTTPS stuff
const server = await Server(argv.dest, {
...argv, single: true
});
Watcher(config, {
...argv,
onError: msg => server.broadcast('error', msg),
onUpdate: arr => server.broadcast('update', JSON.stringify(arr)),
});
}
exports.build = index;
exports.watch = index$1;
declare module 'hottie/server' {
import type { Server } from 'http';
import type { Options } from 'sirv';
declare function broadcast(event: string, data: string): void;
declare function init(dir: string, options?: Options): Server & {
broadcast: typeof broadcast
};
export default init;
}

Sorry, the diff of this file is not supported yet

var http = require('http');
var sirv = require('sirv');
var parse = require('@polka/url');
const clients = new Map();
function start(dir, opts={}) {
let assets = sirv(dir, { ...opts, dev: true });
return function (req, res, next) {
let { pathname } = parse(req);
if (pathname !== '/_hmr') {
return assets(req, res, next);
}
res.writeHead(200, {
'connection': 'keep-alive',
'content-type': 'text/event-stream',
'transfer-encoding': 'identity',
'cache-control': 'no-cache',
});
let key = Math.random().toString(36).slice(2);
let message = format(key, 'start');
res.connection.setTimeout(0);
clients.set(key, res);
req.once('close', () => {
clients.delete(key);
res.end();
});
res.write(message);
};
}
function format(data, event, id) {
let msg = '';
if (id) msg += `id: ${id}\n`;
if (event) msg += `event: hmr:${event}\n`;
return msg + `data: ${data}\n\n`;
}
function broadcast(event, data) {
let msg = format(data, event);
for (let [, res] of clients) res.write(msg);
}
function stop(callback) {
for (let [, res] of clients) res.end();
if (callback) callback();
}
function index (dir, opts = {}) {
const handler = start(dir, opts);
const server = http.createServer(handler);
const close = server.close.bind(server);
server.broadcast = broadcast;
server.close = (db) => {
stop();
return close(db);
};
return server;
}
module.exports = index;
import { createServer } from 'http';
import sirv from 'sirv';
import parse from '@polka/url';
const clients = new Map();
function start(dir, opts={}) {
let assets = sirv(dir, { ...opts, dev: true });
return function (req, res, next) {
let { pathname } = parse(req);
if (pathname !== '/_hmr') {
return assets(req, res, next);
}
res.writeHead(200, {
'connection': 'keep-alive',
'content-type': 'text/event-stream',
'transfer-encoding': 'identity',
'cache-control': 'no-cache',
});
let key = Math.random().toString(36).slice(2);
let message = format(key, 'start');
res.connection.setTimeout(0);
clients.set(key, res);
req.once('close', () => {
clients.delete(key);
res.end();
});
res.write(message);
};
}
function format(data, event, id) {
let msg = '';
if (id) msg += `id: ${id}\n`;
if (event) msg += `event: hmr:${event}\n`;
return msg + `data: ${data}\n\n`;
}
function broadcast(event, data) {
let msg = format(data, event);
for (let [, res] of clients) res.write(msg);
}
function stop(callback) {
for (let [, res] of clients) res.end();
if (callback) callback();
}
function index (dir, opts = {}) {
const handler = start(dir, opts);
const server = createServer(handler);
const close = server.close.bind(server);
server.broadcast = broadcast;
server.close = (db) => {
stop();
return close(db);
};
return server;
}
export default index;
+53
-2
{
"name": "hottie",
"version": "0.0.0"
"name": "hottie",
"version": "0.0.1",
"repository": "lukeed/hottie",
"description": "WIP",
"license": "MIT",
"types": "index.d.ts",
"bin": {
"hottie": "bin.js"
},
"author": {
"name": "Luke Edwards",
"email": "luke.edwards05@gmail.com",
"url": "https://lukeed.com"
},
"files": [
"*.d.ts",
"client",
"commands",
"server",
"bin.js"
],
"exports": {
"./server": {
"import": "./server/index.mjs",
"require": "./server/index.js"
}
},
"engines": {
"node": ">= 10"
},
"scripts": {
"build": "node build",
"prepublishOnly": "node build",
"test": "uvu -r esm packages test"
},
"dependencies": {
"@polka/url": "1.0.0-next.11",
"@rollup/plugin-commonjs": "^12.0.0",
"@rollup/plugin-json": "^4.0.3",
"@rollup/plugin-node-resolve": "^8.0.0",
"kleur": "^3.0.3",
"local-access": "^1.0.1",
"node-html-parser": "^1.2.18",
"rollup": "^2.13.0",
"rollup-plugin-terser": "^6.1.0",
"sade": "^1.7.0",
"sirv": "1.0.0-next.9",
"totalist": "^1.0.1"
},
"devDependencies": {
"esm": "^3.2.25",
"uvu": "^0.0.11"
}
}
-1
//