larry babby and threejs for glsl

This commit is contained in:
Sam
2024-06-24 21:24:00 +12:00
parent 87d5dc634d
commit 907ebae4c0
6474 changed files with 1279596 additions and 8 deletions

View File

@@ -0,0 +1,298 @@
// @flow
import type {ReporterEvent, PluginOptions} from '@parcel/types';
import type {Diagnostic} from '@parcel/diagnostic';
import type {Color} from 'chalk';
import {Reporter} from '@parcel/plugin';
import {
getProgressMessage,
prettifyTime,
prettyDiagnostic,
throttle,
} from '@parcel/utils';
import chalk from 'chalk';
import {getTerminalWidth} from './utils';
import logLevels from './logLevels';
import bundleReport from './bundleReport';
import phaseReport from './phaseReport';
import {
writeOut,
updateSpinner,
persistSpinner,
isTTY,
resetWindow,
persistMessage,
} from './render';
import * as emoji from './emoji';
import wrapAnsi from 'wrap-ansi';
const THROTTLE_DELAY = 100;
const seenWarnings = new Set();
const seenPhases = new Set();
const seenPhasesGen = new Set();
let phaseStartTimes = {};
let pendingIncrementalBuild = false;
let statusThrottle = throttle((message: string) => {
updateSpinner(message);
}, THROTTLE_DELAY);
// Exported only for test
export async function _report(
event: ReporterEvent,
options: PluginOptions,
): Promise<void> {
let logLevelFilter = logLevels[options.logLevel || 'info'];
switch (event.type) {
case 'buildStart': {
seenWarnings.clear();
seenPhases.clear();
if (logLevelFilter < logLevels.info) {
break;
}
// Clear any previous output
resetWindow();
if (options.serveOptions) {
persistMessage(
chalk.blue.bold(
`Server running at ${
options.serveOptions.https ? 'https' : 'http'
}://${options.serveOptions.host ?? 'localhost'}:${
options.serveOptions.port
}`,
),
);
}
break;
}
case 'buildProgress': {
if (logLevelFilter < logLevels.info) {
break;
}
if (pendingIncrementalBuild) {
pendingIncrementalBuild = false;
phaseStartTimes = {};
seenPhasesGen.clear();
seenPhases.clear();
}
if (!seenPhasesGen.has(event.phase)) {
phaseStartTimes[event.phase] = Date.now();
seenPhasesGen.add(event.phase);
}
if (!isTTY && logLevelFilter != logLevels.verbose) {
if (event.phase == 'transforming' && !seenPhases.has('transforming')) {
updateSpinner('Building...');
} else if (event.phase == 'bundling' && !seenPhases.has('bundling')) {
updateSpinner('Bundling...');
} else if (
(event.phase == 'packaging' || event.phase == 'optimizing') &&
!seenPhases.has('packaging') &&
!seenPhases.has('optimizing')
) {
updateSpinner('Packaging & Optimizing...');
}
seenPhases.add(event.phase);
break;
}
let message = getProgressMessage(event);
if (message != null) {
if (isTTY) {
statusThrottle(chalk.gray.bold(message));
} else {
updateSpinner(message);
}
}
break;
}
case 'buildSuccess':
if (logLevelFilter < logLevels.info) {
break;
}
phaseStartTimes['buildSuccess'] = Date.now();
persistSpinner(
'buildProgress',
'success',
chalk.green.bold(`Built in ${prettifyTime(event.buildTime)}`),
);
if (options.mode === 'production') {
await bundleReport(
event.bundleGraph,
options.outputFS,
options.projectRoot,
options.detailedReport?.assetsPerBundle,
);
} else {
pendingIncrementalBuild = true;
}
if (process.env.PARCEL_SHOW_PHASE_TIMES) {
phaseReport(phaseStartTimes);
}
break;
case 'buildFailure':
if (logLevelFilter < logLevels.error) {
break;
}
resetWindow();
persistSpinner('buildProgress', 'error', chalk.red.bold('Build failed.'));
await writeDiagnostic(options, event.diagnostics, 'red', true);
break;
case 'cache':
if (event.size > 500000) {
switch (event.phase) {
case 'start':
updateSpinner('Writing cache to disk');
break;
case 'end':
persistSpinner(
'cache',
'success',
chalk.grey.bold(`Cache written to disk`),
);
break;
}
}
break;
case 'log': {
if (logLevelFilter < logLevels[event.level]) {
break;
}
switch (event.level) {
case 'success':
writeOut(chalk.green(event.message));
break;
case 'progress':
writeOut(event.message);
break;
case 'verbose':
case 'info':
await writeDiagnostic(options, event.diagnostics, 'blue');
break;
case 'warn':
if (
event.diagnostics.some(
diagnostic => !seenWarnings.has(diagnostic.message),
)
) {
await writeDiagnostic(options, event.diagnostics, 'yellow', true);
for (let diagnostic of event.diagnostics) {
seenWarnings.add(diagnostic.message);
}
}
break;
case 'error':
await writeDiagnostic(options, event.diagnostics, 'red', true);
break;
default:
throw new Error('Unknown log level ' + event.level);
}
}
}
}
async function writeDiagnostic(
options: PluginOptions,
diagnostics: Array<Diagnostic>,
color: Color,
isError: boolean = false,
) {
let columns = getTerminalWidth().columns;
let indent = 2;
let spaceAfter = isError;
for (let diagnostic of diagnostics) {
let {message, stack, codeframe, hints, documentation} =
await prettyDiagnostic(diagnostic, options, columns - indent);
// $FlowFixMe[incompatible-use]
message = chalk[color](message);
if (spaceAfter) {
writeOut('');
}
if (message) {
writeOut(wrapWithIndent(message), isError);
}
if (stack || codeframe) {
writeOut('');
}
if (stack) {
writeOut(chalk.gray(wrapWithIndent(stack, indent)), isError);
}
if (codeframe) {
writeOut(indentString(codeframe, indent), isError);
}
if ((stack || codeframe) && (hints.length > 0 || documentation)) {
writeOut('');
}
// Write hints
let hintIndent = stack || codeframe ? indent : 0;
for (let hint of hints) {
writeOut(
wrapWithIndent(
`${emoji.hint} ${chalk.blue.bold(hint)}`,
hintIndent + 3,
hintIndent,
),
);
}
if (documentation) {
writeOut(
wrapWithIndent(
`${emoji.docs} ${chalk.magenta.bold(documentation)}`,
hintIndent + 3,
hintIndent,
),
);
}
spaceAfter = stack || codeframe || hints.length > 0 || documentation;
}
if (spaceAfter) {
writeOut('');
}
}
function wrapWithIndent(string, indent = 0, initialIndent = indent) {
let width = getTerminalWidth().columns;
return indentString(
wrapAnsi(string.trimEnd(), width - indent, {trim: false}),
indent,
initialIndent,
);
}
function indentString(string, indent = 0, initialIndent = indent) {
return (
' '.repeat(initialIndent) + string.replace(/\n/g, '\n' + ' '.repeat(indent))
);
}
export default (new Reporter({
report({event, options}) {
return _report(event, options);
},
}): Reporter);

View File

@@ -0,0 +1,99 @@
// @flow
import type {BundleGraph, FilePath, PackagedBundle} from '@parcel/types';
import type {FileSystem} from '@parcel/fs';
import {generateBuildMetrics, prettifyTime} from '@parcel/utils';
import filesize from 'filesize';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
import * as emoji from './emoji';
import {writeOut, table} from './render';
import {formatFilename} from './utils';
const LARGE_BUNDLE_SIZE = 1024 * 1024;
const COLUMNS = [
{align: 'left'}, // name
{align: 'right'}, // size
{align: 'right'}, // time
];
export default async function bundleReport(
bundleGraph: BundleGraph<PackagedBundle>,
fs: FileSystem,
projectRoot: FilePath,
assetCount: number = 0,
) {
let bundleList = bundleGraph.getBundles();
// Get a list of bundles sorted by size
let {bundles} =
assetCount > 0
? await generateBuildMetrics(bundleList, fs, projectRoot)
: {
bundles: bundleList.map(b => {
return {
filePath: nullthrows(b.filePath),
size: b.stats.size,
time: b.stats.time,
assets: [],
};
}),
};
let rows = [];
for (let bundle of bundles) {
// Add a row for the bundle
rows.push([
formatFilename(bundle.filePath || '', chalk.cyan.bold),
chalk.bold(prettifySize(bundle.size, bundle.size > LARGE_BUNDLE_SIZE)),
chalk.green.bold(prettifyTime(bundle.time)),
]);
if (assetCount > 0) {
let largestAssets = bundle.assets.slice(0, assetCount);
for (let asset of largestAssets) {
let columns: Array<string> = [
asset == largestAssets[largestAssets.length - 1] ? '└── ' : '├── ',
chalk.dim(prettifySize(asset.size)),
chalk.dim(chalk.green(prettifyTime(asset.time))),
];
if (asset.filePath !== '') {
columns[0] += formatFilename(asset.filePath, chalk.reset);
} else {
columns[0] += 'Code from unknown sourcefiles';
}
// Add a row for the asset.
rows.push(columns);
}
if (bundle.assets.length > largestAssets.length) {
rows.push([
'└── ' +
chalk.dim(
`+ ${bundle.assets.length - largestAssets.length} more assets`,
),
]);
}
// If this isn't the last bundle, add an empty row before the next one
if (bundle !== bundles[bundles.length - 1]) {
rows.push([]);
}
}
}
// Render table
writeOut('');
table(COLUMNS, rows);
}
function prettifySize(size, isLarge) {
let res = filesize(size);
if (isLarge) {
return chalk.yellow(emoji.warning + ' ' + res);
}
return chalk.magenta(res);
}

View File

@@ -0,0 +1,29 @@
// @flow strict-local
// From https://github.com/sindresorhus/is-unicode-supported/blob/8f123916d5c25a87c4f966dcc248b7ca5df2b4ca/index.js
// This package is ESM-only so it has to be vendored
function isUnicodeSupported() {
if (process.platform !== 'win32') {
return process.env.TERM !== 'linux'; // Linux console (kernel)
}
return (
Boolean(process.env.CI) ||
Boolean(process.env.WT_SESSION) || // Windows Terminal
process.env.ConEmuTask === '{cmd::Cmder}' || // ConEmu and cmder
process.env.TERM_PROGRAM === 'vscode' ||
process.env.TERM === 'xterm-256color' ||
process.env.TERM === 'alacritty'
);
}
const supportsEmoji = isUnicodeSupported();
// Fallback symbols for Windows from https://en.wikipedia.org/wiki/Code_page_437
export const progress: string = supportsEmoji ? '⏳' : '∞';
export const success: string = supportsEmoji ? '✨' : '√';
export const error: string = supportsEmoji ? '🚨' : '×';
export const warning: string = supportsEmoji ? '⚠️' : '‼';
export const info: string = supportsEmoji ? '' : '';
export const hint: string = supportsEmoji ? '💡' : '';
export const docs: string = supportsEmoji ? '📝' : '';

View File

@@ -0,0 +1,13 @@
// @flow strict-local
const logLevels = {
none: 0,
error: 1,
warn: 2,
info: 3,
progress: 3,
success: 3,
verbose: 4,
};
export default logLevels;

View File

@@ -0,0 +1,33 @@
// @flow
import {prettifyTime} from '@parcel/utils';
import chalk from 'chalk';
import {writeOut} from './render';
import invariant from 'assert';
export default function phaseReport(phaseStartTimes: {[string]: number}) {
let phaseTimes = {};
if (phaseStartTimes['transforming'] && phaseStartTimes['bundling']) {
phaseTimes['Transforming'] =
phaseStartTimes['bundling'] - phaseStartTimes['transforming'];
}
let packagingAndOptimizing =
phaseStartTimes['packaging'] && phaseStartTimes['optimizing']
? Math.min(phaseStartTimes['packaging'], phaseStartTimes['optimizing'])
: phaseStartTimes['packaging'] || phaseStartTimes['optimizing'];
if (phaseStartTimes['bundling'] && packagingAndOptimizing) {
phaseTimes['Bundling'] =
packagingAndOptimizing - phaseStartTimes['bundling'];
}
if (packagingAndOptimizing && phaseStartTimes['buildSuccess']) {
phaseTimes['Packaging & Optimizing'] =
phaseStartTimes['buildSuccess'] - packagingAndOptimizing;
}
for (let [phase, time] of Object.entries(phaseTimes)) {
invariant(typeof time === 'number');
writeOut(chalk.green.bold(`${phase} finished in ${prettifyTime(time)}`));
}
}

View File

@@ -0,0 +1,149 @@
// @flow
import type {Writable} from 'stream';
import readline from 'readline';
import ora from 'ora';
import stringWidth from 'string-width';
import type {PadAlign} from './utils';
import {pad, countLines} from './utils';
import * as emoji from './emoji';
type ColumnType = {|
align: PadAlign,
|};
export const isTTY: any | boolean | true =
// $FlowFixMe
process.env.NODE_ENV !== 'test' && process.stdout.isTTY;
let stdout = process.stdout;
let stderr = process.stderr;
// Some state so we clear the output properly
let lineCount = 0;
let errorLineCount = 0;
let statusPersisted = false;
export function _setStdio(stdoutLike: Writable, stderrLike: Writable) {
stdout = stdoutLike;
stderr = stderrLike;
}
let spinner = ora({
color: 'green',
stream: stdout,
discardStdin: false,
});
let persistedMessages = [];
export function writeOut(message: string, isError: boolean = false) {
let processedMessage = message + '\n';
let hasSpinner = spinner.isSpinning;
// Stop spinner so we don't duplicate it
if (hasSpinner) {
spinner.stop();
}
let lines = countLines(message);
if (isError) {
stderr.write(processedMessage);
errorLineCount += lines;
} else {
stdout.write(processedMessage);
lineCount += lines;
}
// Restart the spinner
if (hasSpinner) {
spinner.start();
}
}
export function persistMessage(message: string) {
if (persistedMessages.includes(message)) return;
persistedMessages.push(message);
writeOut(message);
}
export function updateSpinner(message: string) {
// This helps the spinner play well with the tests
if (!isTTY) {
writeOut(message);
return;
}
spinner.text = message + '\n';
if (!spinner.isSpinning) {
spinner.start();
}
}
export function persistSpinner(
name: string,
status: 'success' | 'error',
message: string,
) {
spinner.stopAndPersist({
symbol: emoji[status],
text: message,
});
statusPersisted = true;
}
function clearStream(stream: Writable, lines: number) {
if (!isTTY) return;
readline.moveCursor(stream, 0, -lines);
readline.clearScreenDown(stream);
}
// Reset the window's state
export function resetWindow() {
if (!isTTY) return;
// If status has been persisted we add a line
// Otherwise final states would remain in the terminal for rebuilds
if (statusPersisted) {
lineCount++;
statusPersisted = false;
}
clearStream(stderr, errorLineCount);
errorLineCount = 0;
clearStream(stdout, lineCount);
lineCount = 0;
for (let m of persistedMessages) {
writeOut(m);
}
}
export function table(columns: Array<ColumnType>, table: Array<Array<string>>) {
// Measure column widths
let colWidths = [];
for (let row of table) {
let i = 0;
for (let item of row) {
colWidths[i] = Math.max(colWidths[i] || 0, stringWidth(item));
i++;
}
}
// Render rows
for (let row of table) {
let items = row.map((item, i) => {
// Add padding between columns unless the alignment is the opposite to the
// next column and pad to the column width.
let padding =
!columns[i + 1] || columns[i + 1].align === columns[i].align ? 4 : 0;
return pad(item, colWidths[i] + padding, columns[i].align);
});
writeOut(items.join(''));
}
}

View File

@@ -0,0 +1,48 @@
// @flow
import path from 'path';
import chalk from 'chalk';
import stringWidth from 'string-width';
import termSize from 'term-size';
import {stripAnsi} from '@parcel/utils';
export type PadAlign = 'left' | 'right';
let terminalSize = termSize();
process.stdout.on('resize', function () {
terminalSize = termSize();
});
export function getTerminalWidth(): any {
return terminalSize;
}
// Pad a string with spaces on either side
export function pad(
text: string,
length: number,
align: PadAlign = 'left',
): string {
let pad = ' '.repeat(length - stringWidth(text));
if (align === 'right') {
return pad + text;
}
return text + pad;
}
export function formatFilename(
filename: string,
color: (s: string) => string = chalk.reset,
): string {
let dir = path.relative(process.cwd(), path.dirname(filename));
return (
chalk.dim(dir + (dir ? path.sep : '')) + color(path.basename(filename))
);
}
export function countLines(message: string): number {
let {columns} = terminalSize;
return stripAnsi(message)
.split('\n')
.reduce((p, line) => p + Math.ceil((stringWidth(line) || 1) / columns), 0);
}