WIP: bootstrap and partial real Solana watcher implementation
This commit is contained in:
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"p521.js","sourceRoot":"","sources":["src/p521.ts"],"names":[],"mappings":";;;AAMA,uCAAuD;AACvD,sEAAsE;AACzD,QAAA,IAAI,GAAiB,cAAK,CAAC;AACxC,sEAAsE;AACzD,QAAA,SAAS,GAAiB,cAAK,CAAC;AAC7C,6EAA6E;AAChE,QAAA,WAAW,GAAsC,CAAC,GAAG,EAAE,CAAC,qBAAW,CAAC,WAAW,CAAC,EAAE,CAAC;AAChG,6EAA6E;AAChE,QAAA,aAAa,GAAsC,CAAC,GAAG,EAAE,CAAC,qBAAW,CAAC,aAAa,CAAC,EAAE,CAAC"}
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { Node, SourceFile } from "../../ast/index.ts";
|
||||
/**
|
||||
* Encode a SourceFile AST node into the binary format.
|
||||
*/
|
||||
export declare function encodeSourceFile(sourceFile: SourceFile): Uint8Array;
|
||||
/**
|
||||
* Encode an arbitrary AST node into the binary format.
|
||||
* When encoding a non-SourceFile node, the header hash and parse options fields will be zero.
|
||||
*/
|
||||
export declare function encodeNode(node: Node): Uint8Array;
|
||||
/**
|
||||
* Encode a Uint8Array to a base64 string.
|
||||
*/
|
||||
export declare function uint8ArrayToBase64(data: Uint8Array): string;
|
||||
//# sourceMappingURL=encoder.d.ts.map
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Codec, Decoder, Encoder, FixedSizeCodec, FixedSizeDecoder, FixedSizeEncoder, VariableSizeCodec, VariableSizeDecoder, VariableSizeEncoder } from './codec';
|
||||
import { ReadonlyUint8Array } from './readonly-uint8array';
|
||||
/**
|
||||
* Creates an encoder that writes a `Uint8Array` sentinel after the encoded value.
|
||||
* This is useful to delimit the encoded value when being read by a decoder.
|
||||
*
|
||||
* See {@link addCodecSentinel} for more information.
|
||||
*
|
||||
* @typeParam TFrom - The type of the value to encode.
|
||||
*
|
||||
* @see {@link addCodecSentinel}
|
||||
*/
|
||||
export declare function addEncoderSentinel<TFrom>(encoder: FixedSizeEncoder<TFrom>, sentinel: ReadonlyUint8Array): FixedSizeEncoder<TFrom>;
|
||||
export declare function addEncoderSentinel<TFrom>(encoder: Encoder<TFrom>, sentinel: ReadonlyUint8Array): VariableSizeEncoder<TFrom>;
|
||||
/**
|
||||
* Creates a decoder that continues reading until
|
||||
* a given `Uint8Array` sentinel is found.
|
||||
*
|
||||
* See {@link addCodecSentinel} for more information.
|
||||
*
|
||||
* @typeParam TTo - The type of the decoded value.
|
||||
*
|
||||
* @see {@link addCodecSentinel}
|
||||
*/
|
||||
export declare function addDecoderSentinel<TTo>(decoder: FixedSizeDecoder<TTo>, sentinel: ReadonlyUint8Array): FixedSizeDecoder<TTo>;
|
||||
export declare function addDecoderSentinel<TTo>(decoder: Decoder<TTo>, sentinel: ReadonlyUint8Array): VariableSizeDecoder<TTo>;
|
||||
/**
|
||||
* Creates a Codec that writes a given `Uint8Array` sentinel after the encoded
|
||||
* value and, when decoding, continues reading until the sentinel is found.
|
||||
*
|
||||
* This sets a limit on variable-size codecs and tells us when to stop decoding.
|
||||
*
|
||||
* @typeParam TFrom - The type of the value to encode.
|
||||
* @typeParam TTo - The type of the decoded value.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const codec = addCodecSentinel(getUtf8Codec(), new Uint8Array([255, 255]));
|
||||
* codec.encode('hello');
|
||||
* // 0x68656c6c6fffff
|
||||
* // | └-- Our sentinel.
|
||||
* // └-- Our encoded string.
|
||||
* ```
|
||||
*
|
||||
* @remarks
|
||||
* Note that the sentinel _must not_ be present in the encoded data and
|
||||
* _must_ be present in the decoded data for this to work.
|
||||
* If this is not the case, dedicated errors will be thrown.
|
||||
*
|
||||
* ```ts
|
||||
* const sentinel = new Uint8Array([108, 108]); // 'll'
|
||||
* const codec = addCodecSentinel(getUtf8Codec(), sentinel);
|
||||
*
|
||||
* codec.encode('hello'); // Throws: sentinel is in encoded data.
|
||||
* codec.decode(new Uint8Array([1, 2, 3])); // Throws: sentinel missing in decoded data.
|
||||
* ```
|
||||
*
|
||||
* Separate {@link addEncoderSentinel} and {@link addDecoderSentinel} functions are also available.
|
||||
*
|
||||
* ```ts
|
||||
* const bytes = addEncoderSentinel(getUtf8Encoder(), sentinel).encode('hello');
|
||||
* const value = addDecoderSentinel(getUtf8Decoder(), sentinel).decode(bytes);
|
||||
* ```
|
||||
*
|
||||
* @see {@link addEncoderSentinel}
|
||||
* @see {@link addDecoderSentinel}
|
||||
*/
|
||||
export declare function addCodecSentinel<TFrom, TTo extends TFrom>(codec: FixedSizeCodec<TFrom, TTo>, sentinel: ReadonlyUint8Array): FixedSizeCodec<TFrom, TTo>;
|
||||
export declare function addCodecSentinel<TFrom, TTo extends TFrom>(codec: Codec<TFrom, TTo>, sentinel: ReadonlyUint8Array): VariableSizeCodec<TFrom, TTo>;
|
||||
//# sourceMappingURL=add-codec-sentinel.d.ts.map
|
||||
@@ -0,0 +1,325 @@
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import * as z from "zod/v4";
|
||||
|
||||
test("z.number() basic validation", () => {
|
||||
const schema = z.number();
|
||||
expect(schema.parse(1234)).toEqual(1234);
|
||||
});
|
||||
|
||||
test("NaN validation", () => {
|
||||
const schema = z.number();
|
||||
expect(() => schema.parse(Number.NaN)).toThrow();
|
||||
});
|
||||
|
||||
test("Infinity validation", () => {
|
||||
const schema = z.number();
|
||||
expect(schema.safeParse(Number.POSITIVE_INFINITY)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"expected": "number",
|
||||
"code": "invalid_type",
|
||||
"received": "Infinity",
|
||||
"path": [],
|
||||
"message": "Invalid input: expected number, received number"
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
expect(schema.safeParse(Number.NEGATIVE_INFINITY)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"expected": "number",
|
||||
"code": "invalid_type",
|
||||
"received": "Infinity",
|
||||
"path": [],
|
||||
"message": "Invalid input: expected number, received number"
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test(".gt() validation", () => {
|
||||
const schema = z.number().gt(0).gt(5);
|
||||
expect(schema.parse(6)).toEqual(6);
|
||||
expect(() => schema.parse(5)).toThrow();
|
||||
});
|
||||
|
||||
test(".gte() validation", () => {
|
||||
const schema = z.number().gt(0).gte(1).gte(5);
|
||||
expect(schema.parse(5)).toEqual(5);
|
||||
expect(() => schema.parse(4)).toThrow();
|
||||
});
|
||||
|
||||
test(".min() validation", () => {
|
||||
const schema = z.number().min(0).min(5);
|
||||
expect(schema.parse(5)).toEqual(5);
|
||||
expect(() => schema.parse(4)).toThrow();
|
||||
});
|
||||
|
||||
test(".lt() validation", () => {
|
||||
const schema = z.number().lte(10).lt(5);
|
||||
expect(schema.parse(4)).toEqual(4);
|
||||
expect(() => schema.parse(5)).toThrow();
|
||||
});
|
||||
|
||||
test(".lte() validation", () => {
|
||||
const schema = z.number().lte(10).lte(5);
|
||||
expect(schema.parse(5)).toEqual(5);
|
||||
expect(() => schema.parse(6)).toThrow();
|
||||
});
|
||||
|
||||
test(".max() validation", () => {
|
||||
const schema = z.number().max(10).max(5);
|
||||
expect(schema.parse(5)).toEqual(5);
|
||||
expect(() => schema.parse(6)).toThrow();
|
||||
});
|
||||
|
||||
test(".int() validation", () => {
|
||||
const schema = z.number().int();
|
||||
expect(schema.parse(4)).toEqual(4);
|
||||
expect(() => schema.parse(3.14)).toThrow();
|
||||
});
|
||||
|
||||
test(".positive() validation", () => {
|
||||
const schema = z.number().positive();
|
||||
expect(schema.parse(1)).toEqual(1);
|
||||
expect(() => schema.parse(0)).toThrow();
|
||||
expect(() => schema.parse(-1)).toThrow();
|
||||
});
|
||||
|
||||
test(".negative() validation", () => {
|
||||
const schema = z.number().negative();
|
||||
expect(schema.parse(-1)).toEqual(-1);
|
||||
expect(() => schema.parse(0)).toThrow();
|
||||
expect(() => schema.parse(1)).toThrow();
|
||||
});
|
||||
|
||||
test(".nonpositive() validation", () => {
|
||||
const schema = z.number().nonpositive();
|
||||
expect(schema.parse(0)).toEqual(0);
|
||||
expect(schema.parse(-1)).toEqual(-1);
|
||||
expect(() => schema.parse(1)).toThrow();
|
||||
});
|
||||
|
||||
test(".nonnegative() validation", () => {
|
||||
const schema = z.number().nonnegative();
|
||||
expect(schema.parse(0)).toEqual(0);
|
||||
expect(schema.parse(1)).toEqual(1);
|
||||
expect(() => schema.parse(-1)).toThrow();
|
||||
});
|
||||
|
||||
test("multipleOf", () => {
|
||||
const numbers = {
|
||||
number3: 5.123,
|
||||
number6: 5.123456,
|
||||
number7: 5.1234567,
|
||||
number8: 5.12345678,
|
||||
};
|
||||
|
||||
const schemas = {
|
||||
schema6: z.number().multipleOf(0.000001),
|
||||
schema7: z.number().multipleOf(0.0000001),
|
||||
};
|
||||
|
||||
expect(() => schemas.schema6.parse(numbers.number3)).not.toThrow();
|
||||
expect(() => schemas.schema6.parse(numbers.number6)).not.toThrow();
|
||||
expect(() => schemas.schema6.parse(numbers.number7)).toThrow();
|
||||
expect(() => schemas.schema6.parse(numbers.number8)).toThrow();
|
||||
expect(() => schemas.schema7.parse(numbers.number3)).not.toThrow();
|
||||
expect(() => schemas.schema7.parse(numbers.number6)).not.toThrow();
|
||||
expect(() => schemas.schema7.parse(numbers.number7)).not.toThrow();
|
||||
expect(() => schemas.schema7.parse(numbers.number8)).toThrow();
|
||||
});
|
||||
|
||||
test(".multipleOf() with positive divisor", () => {
|
||||
const schema = z.number().multipleOf(5);
|
||||
expect(schema.parse(15)).toEqual(15);
|
||||
expect(schema.parse(-15)).toEqual(-15);
|
||||
expect(() => schema.parse(7.5)).toThrow();
|
||||
expect(() => schema.parse(-7.5)).toThrow();
|
||||
});
|
||||
|
||||
test(".multipleOf() with negative divisor", () => {
|
||||
const schema = z.number().multipleOf(-5);
|
||||
expect(schema.parse(-15)).toEqual(-15);
|
||||
expect(schema.parse(15)).toEqual(15);
|
||||
expect(() => schema.parse(-7.5)).toThrow();
|
||||
expect(() => schema.parse(7.5)).toThrow();
|
||||
});
|
||||
|
||||
test(".multipleOf() with scientific notation (multi-digit exponents)", () => {
|
||||
// Regression test for https://github.com/colinhacks/zod/pull/5687
|
||||
// The regex was using \d? which only matches single-digit exponents
|
||||
const schema = z.number().multipleOf(1e-10);
|
||||
|
||||
// These should all pass - they are valid multiples of 1e-10
|
||||
expect(schema.parse(1e-10)).toEqual(1e-10);
|
||||
expect(schema.parse(5e-10)).toEqual(5e-10);
|
||||
expect(schema.parse(1e-9)).toEqual(1e-9); // 10 * 1e-10
|
||||
|
||||
// Test with 1e-15 (exponent = 15, two digits)
|
||||
const schema15 = z.number().multipleOf(1e-15);
|
||||
expect(schema15.parse(1e-15)).toEqual(1e-15);
|
||||
expect(schema15.parse(3e-15)).toEqual(3e-15);
|
||||
});
|
||||
|
||||
test(".multipleOf() with small floats / scientific notation (#5792)", () => {
|
||||
const schema = z.number().multipleOf(1e-7);
|
||||
|
||||
// Valid multiples (integer * 1e-7)
|
||||
expect(schema.safeParse(0).success).toBe(true);
|
||||
expect(schema.safeParse(1e-7).success).toBe(true);
|
||||
expect(schema.safeParse(2e-7).success).toBe(true);
|
||||
expect(schema.safeParse(3e-7).success).toBe(true);
|
||||
|
||||
// Invalid — 2.5 and 1.5 are not integers
|
||||
expect(schema.safeParse(2.5e-7).success).toBe(false);
|
||||
expect(schema.safeParse(1.5e-7).success).toBe(false);
|
||||
});
|
||||
|
||||
test(".step() validation", () => {
|
||||
const schemaPointOne = z.number().step(0.1);
|
||||
const schemaPointZeroZeroZeroOne = z.number().step(0.0001);
|
||||
const schemaSixPointFour = z.number().step(6.4);
|
||||
|
||||
expect(schemaPointOne.parse(6)).toEqual(6);
|
||||
expect(schemaPointOne.parse(6.1)).toEqual(6.1);
|
||||
expect(schemaSixPointFour.parse(12.8)).toEqual(12.8);
|
||||
expect(schemaPointZeroZeroZeroOne.parse(3.01)).toEqual(3.01);
|
||||
expect(() => schemaPointOne.parse(6.11)).toThrow();
|
||||
expect(() => schemaPointOne.parse(6.1000000001)).toThrow();
|
||||
expect(() => schemaSixPointFour.parse(6.41)).toThrow();
|
||||
});
|
||||
|
||||
test(".finite() validation", () => {
|
||||
const schema = z.number().finite();
|
||||
expect(schema.parse(123)).toEqual(123);
|
||||
expect(schema.safeParse(Number.POSITIVE_INFINITY)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"expected": "number",
|
||||
"code": "invalid_type",
|
||||
"received": "Infinity",
|
||||
"path": [],
|
||||
"message": "Invalid input: expected number, received number"
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
expect(schema.safeParse(Number.NEGATIVE_INFINITY)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"expected": "number",
|
||||
"code": "invalid_type",
|
||||
"received": "Infinity",
|
||||
"path": [],
|
||||
"message": "Invalid input: expected number, received number"
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test(".safe() validation", () => {
|
||||
const schema = z.number().safe();
|
||||
expect(schema.parse(Number.MIN_SAFE_INTEGER)).toEqual(Number.MIN_SAFE_INTEGER);
|
||||
expect(schema.parse(Number.MAX_SAFE_INTEGER)).toEqual(Number.MAX_SAFE_INTEGER);
|
||||
expect(() => schema.parse(Number.MIN_SAFE_INTEGER - 1)).toThrow();
|
||||
expect(() => schema.parse(Number.MAX_SAFE_INTEGER + 1)).toThrow();
|
||||
});
|
||||
|
||||
test("min value getters", () => {
|
||||
expect(z.number().minValue).toBeNull;
|
||||
expect(z.number().lt(5).minValue).toBeNull;
|
||||
expect(z.number().lte(5).minValue).toBeNull;
|
||||
expect(z.number().max(5).minValue).toBeNull;
|
||||
expect(z.number().negative().minValue).toBeNull;
|
||||
expect(z.number().nonpositive().minValue).toBeNull;
|
||||
expect(z.number().int().minValue).toBeNull;
|
||||
expect(z.number().multipleOf(5).minValue).toBeNull;
|
||||
expect(z.number().finite().minValue).toBeNull;
|
||||
expect(z.number().gt(5).minValue).toEqual(5);
|
||||
expect(z.number().gte(5).minValue).toEqual(5);
|
||||
expect(z.number().min(5).minValue).toEqual(5);
|
||||
expect(z.number().min(5).min(10).minValue).toEqual(10);
|
||||
expect(z.number().positive().minValue).toEqual(0);
|
||||
expect(z.number().nonnegative().minValue).toEqual(0);
|
||||
expect(z.number().safe().minValue).toEqual(Number.MIN_SAFE_INTEGER);
|
||||
});
|
||||
|
||||
test("max value getters", () => {
|
||||
expect(z.number().maxValue).toBeNull;
|
||||
expect(z.number().gt(5).maxValue).toBeNull;
|
||||
expect(z.number().gte(5).maxValue).toBeNull;
|
||||
expect(z.number().min(5).maxValue).toBeNull;
|
||||
expect(z.number().positive().maxValue).toBeNull;
|
||||
expect(z.number().nonnegative().maxValue).toBeNull;
|
||||
expect(z.number().int().minValue).toBeNull;
|
||||
expect(z.number().multipleOf(5).minValue).toBeNull;
|
||||
expect(z.number().finite().minValue).toBeNull;
|
||||
expect(z.number().lt(5).maxValue).toEqual(5);
|
||||
expect(z.number().lte(5).maxValue).toEqual(5);
|
||||
expect(z.number().max(5).maxValue).toEqual(5);
|
||||
expect(z.number().max(5).max(1).maxValue).toEqual(1);
|
||||
expect(z.number().negative().maxValue).toEqual(0);
|
||||
expect(z.number().nonpositive().maxValue).toEqual(0);
|
||||
expect(z.number().safe().maxValue).toEqual(Number.MAX_SAFE_INTEGER);
|
||||
});
|
||||
|
||||
test("int getter", () => {
|
||||
expect(z.number().isInt).toEqual(false);
|
||||
expect(z.number().int().isInt).toEqual(true);
|
||||
expect(z.number().safe().isInt).toEqual(true);
|
||||
expect(z.number().multipleOf(5).isInt).toEqual(true);
|
||||
});
|
||||
|
||||
/** In Zod 4, number schemas don't accept infinite values. */
|
||||
test("finite getter", () => {
|
||||
expect(z.number().isFinite).toEqual(true);
|
||||
});
|
||||
|
||||
test("string format methods", () => {
|
||||
const a = z.int32().min(5);
|
||||
expect(a.parse(6)).toEqual(6);
|
||||
expect(() => a.parse(1)).toThrow();
|
||||
});
|
||||
|
||||
test("negative zero edge case", () => {
|
||||
const schema = z.number();
|
||||
const negativeZero = -0;
|
||||
const positiveZero = 0;
|
||||
|
||||
// Both -0 and 0 should be valid (parse succeeds)
|
||||
expect(schema.safeParse(negativeZero).success).toBe(true);
|
||||
expect(schema.safeParse(positiveZero).success).toBe(true);
|
||||
// Note: -0 is normalized to 0 after parsing
|
||||
expect(schema.parse(negativeZero) === 0).toBe(true);
|
||||
expect(schema.parse(positiveZero)).toEqual(0);
|
||||
|
||||
// With positive() constraint, both should be invalid (0 is not positive)
|
||||
const positiveSchema = z.number().positive();
|
||||
expect(() => positiveSchema.parse(negativeZero)).toThrow();
|
||||
expect(() => positiveSchema.parse(positiveZero)).toThrow();
|
||||
|
||||
// With nonnegative(), both should be valid (0 is non-negative)
|
||||
const nonnegativeSchema = z.number().nonnegative();
|
||||
expect(nonnegativeSchema.safeParse(negativeZero).success).toBe(true);
|
||||
expect(nonnegativeSchema.safeParse(positiveZero).success).toBe(true);
|
||||
expect(nonnegativeSchema.parse(negativeZero) === 0).toBe(true);
|
||||
expect(nonnegativeSchema.parse(positiveZero)).toEqual(0);
|
||||
});
|
||||
|
||||
test("error customization", () => {
|
||||
z.number().gte(5, { error: (iss) => "Min: " + iss.minimum.valueOf() });
|
||||
z.number().lte(5, { error: (iss) => "Max: " + iss.maximum.valueOf() });
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,42 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('node:test')
|
||||
const assert = require('node:assert')
|
||||
const { join } = require('node:path')
|
||||
const { createReadStream } = require('node:fs')
|
||||
const { promisify } = require('node:util')
|
||||
const stream = require('node:stream')
|
||||
const execa = require('execa')
|
||||
const split = require('split2')
|
||||
|
||||
const { file } = require('../helper')
|
||||
|
||||
const pipeline = promisify(stream.pipeline)
|
||||
const { Writable } = stream
|
||||
const sleep = promisify(setTimeout)
|
||||
|
||||
const skip = process.env.CI || process.env.CITGM
|
||||
|
||||
test('eight million lines', { skip }, async () => {
|
||||
const destination = file()
|
||||
await execa(process.argv[0], [join(__dirname, '..', 'fixtures', 'transport-many-lines.js'), destination])
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
try {
|
||||
await execa('sync') // Wait for the file to be written to disk
|
||||
} catch {
|
||||
// Just a fallback, this should be unreachable
|
||||
}
|
||||
}
|
||||
await sleep(1_000) // It seems that sync is not enough (even in POSIX systems)
|
||||
|
||||
const toWrite = 8 * 1_000_000
|
||||
let count = 0
|
||||
await pipeline(createReadStream(destination), split(), new Writable({
|
||||
write (chunk, enc, cb) {
|
||||
count++
|
||||
cb()
|
||||
}
|
||||
}))
|
||||
assert.equal(count, toWrite)
|
||||
})
|
||||
@@ -0,0 +1,98 @@
|
||||
'use strict'
|
||||
|
||||
const { join } = require('path')
|
||||
const { fork } = require('child_process')
|
||||
const fs = require('fs')
|
||||
const SonicBoom = require('../')
|
||||
const { file, runTests } = require('./helper')
|
||||
|
||||
runTests(buildTests)
|
||||
|
||||
function buildTests (test, sync) {
|
||||
// Reset the umask for testing
|
||||
process.umask(0o000)
|
||||
|
||||
test('end after reopen', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, minLength: 4096, sync })
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
const after = dest + '-moved'
|
||||
stream.reopen(after)
|
||||
stream.write('after reopen\n')
|
||||
stream.on('finish', () => {
|
||||
t.pass('finish emitted')
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
})
|
||||
})
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
|
||||
test('end after 2x reopen', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, minLength: 4096, sync })
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
stream.reopen(dest + '-moved')
|
||||
const after = dest + '-moved-moved'
|
||||
stream.reopen(after)
|
||||
stream.write('after reopen\n')
|
||||
stream.on('finish', () => {
|
||||
t.pass('finish emitted')
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
})
|
||||
})
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
|
||||
test('end if not ready', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, minLength: 4096, sync })
|
||||
const after = dest + '-moved'
|
||||
stream.reopen(after)
|
||||
stream.write('after reopen\n')
|
||||
stream.on('finish', () => {
|
||||
t.pass('finish emitted')
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
})
|
||||
})
|
||||
stream.end()
|
||||
})
|
||||
|
||||
test('chunk data accordingly', (t) => {
|
||||
t.plan(2)
|
||||
|
||||
const child = fork(join(__dirname, '..', 'fixtures', 'firehose.js'), { silent: true })
|
||||
const str = Buffer.alloc(10000).fill('a').toString()
|
||||
|
||||
let data = ''
|
||||
|
||||
child.stdout.on('data', function (chunk) {
|
||||
data += chunk.toString()
|
||||
})
|
||||
|
||||
child.stdout.on('end', function () {
|
||||
t.equal(data, str)
|
||||
})
|
||||
|
||||
child.on('close', function (code) {
|
||||
t.equal(code, 0)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,835 @@
|
||||
import { E as createBuilder, J as createLogger, dt as require_picocolors, lt as VERSION, mt as __toESM } from "./chunks/node.js";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { inspect } from "node:util";
|
||||
import { performance } from "node:perf_hooks";
|
||||
//#region ../../node_modules/.pnpm/cac@7.0.0/node_modules/cac/dist/index.js
|
||||
function toArr(any) {
|
||||
return any == null ? [] : Array.isArray(any) ? any : [any];
|
||||
}
|
||||
function toVal(out, key, val, opts) {
|
||||
var x, old = out[key], nxt = !!~opts.string.indexOf(key) ? val == null || val === true ? "" : String(val) : typeof val === "boolean" ? val : !!~opts.boolean.indexOf(key) ? val === "false" ? false : val === "true" || (out._.push((x = +val, x * 0 === 0) ? x : val), !!val) : (x = +val, x * 0 === 0) ? x : val;
|
||||
out[key] = old == null ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt];
|
||||
}
|
||||
function lib_default(args, opts) {
|
||||
args = args || [];
|
||||
opts = opts || {};
|
||||
var k, arr, arg, name, val, out = { _: [] };
|
||||
var i = 0, j = 0, idx = 0, len = args.length;
|
||||
const alibi = opts.alias !== void 0;
|
||||
const strict = opts.unknown !== void 0;
|
||||
const defaults = opts.default !== void 0;
|
||||
opts.alias = opts.alias || {};
|
||||
opts.string = toArr(opts.string);
|
||||
opts.boolean = toArr(opts.boolean);
|
||||
if (alibi) for (k in opts.alias) {
|
||||
arr = opts.alias[k] = toArr(opts.alias[k]);
|
||||
for (i = 0; i < arr.length; i++) (opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);
|
||||
}
|
||||
for (i = opts.boolean.length; i-- > 0;) {
|
||||
arr = opts.alias[opts.boolean[i]] || [];
|
||||
for (j = arr.length; j-- > 0;) opts.boolean.push(arr[j]);
|
||||
}
|
||||
for (i = opts.string.length; i-- > 0;) {
|
||||
arr = opts.alias[opts.string[i]] || [];
|
||||
for (j = arr.length; j-- > 0;) opts.string.push(arr[j]);
|
||||
}
|
||||
if (defaults) for (k in opts.default) {
|
||||
name = typeof opts.default[k];
|
||||
arr = opts.alias[k] = opts.alias[k] || [];
|
||||
if (opts[name] !== void 0) {
|
||||
opts[name].push(k);
|
||||
for (i = 0; i < arr.length; i++) opts[name].push(arr[i]);
|
||||
}
|
||||
}
|
||||
const keys = strict ? Object.keys(opts.alias) : [];
|
||||
for (i = 0; i < len; i++) {
|
||||
arg = args[i];
|
||||
if (arg === "--") {
|
||||
out._ = out._.concat(args.slice(++i));
|
||||
break;
|
||||
}
|
||||
for (j = 0; j < arg.length; j++) if (arg.charCodeAt(j) !== 45) break;
|
||||
if (j === 0) out._.push(arg);
|
||||
else if (arg.substring(j, j + 3) === "no-") {
|
||||
name = arg.substring(j + 3);
|
||||
if (strict && !~keys.indexOf(name)) return opts.unknown(arg);
|
||||
out[name] = false;
|
||||
} else {
|
||||
for (idx = j + 1; idx < arg.length; idx++) if (arg.charCodeAt(idx) === 61) break;
|
||||
name = arg.substring(j, idx);
|
||||
val = arg.substring(++idx) || i + 1 === len || ("" + args[i + 1]).charCodeAt(0) === 45 || args[++i];
|
||||
arr = j === 2 ? [name] : name;
|
||||
for (idx = 0; idx < arr.length; idx++) {
|
||||
name = arr[idx];
|
||||
if (strict && !~keys.indexOf(name)) return opts.unknown("-".repeat(j) + name);
|
||||
toVal(out, name, idx + 1 < arr.length || val, opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (defaults) {
|
||||
for (k in opts.default) if (out[k] === void 0) out[k] = opts.default[k];
|
||||
}
|
||||
if (alibi) for (k in out) {
|
||||
arr = opts.alias[k] || [];
|
||||
while (arr.length > 0) out[arr.shift()] = out[k];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function removeBrackets(v) {
|
||||
return v.replace(/[<[].+/, "").trim();
|
||||
}
|
||||
function findAllBrackets(v) {
|
||||
const ANGLED_BRACKET_RE_GLOBAL = /<([^>]+)>/g;
|
||||
const SQUARE_BRACKET_RE_GLOBAL = /\[([^\]]+)\]/g;
|
||||
const res = [];
|
||||
const parse = (match) => {
|
||||
let variadic = false;
|
||||
let value = match[1];
|
||||
if (value.startsWith("...")) {
|
||||
value = value.slice(3);
|
||||
variadic = true;
|
||||
}
|
||||
return {
|
||||
required: match[0].startsWith("<"),
|
||||
value,
|
||||
variadic
|
||||
};
|
||||
};
|
||||
let angledMatch;
|
||||
while (angledMatch = ANGLED_BRACKET_RE_GLOBAL.exec(v)) res.push(parse(angledMatch));
|
||||
let squareMatch;
|
||||
while (squareMatch = SQUARE_BRACKET_RE_GLOBAL.exec(v)) res.push(parse(squareMatch));
|
||||
return res;
|
||||
}
|
||||
function getMriOptions(options) {
|
||||
const result = {
|
||||
alias: {},
|
||||
boolean: []
|
||||
};
|
||||
for (const [index, option] of options.entries()) {
|
||||
if (option.names.length > 1) result.alias[option.names[0]] = option.names.slice(1);
|
||||
if (option.isBoolean) if (option.negated) {
|
||||
if (!options.some((o, i) => {
|
||||
return i !== index && o.names.some((name) => option.names.includes(name)) && typeof o.required === "boolean";
|
||||
})) result.boolean.push(option.names[0]);
|
||||
} else result.boolean.push(option.names[0]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function findLongest(arr) {
|
||||
return arr.sort((a, b) => {
|
||||
return a.length > b.length ? -1 : 1;
|
||||
})[0];
|
||||
}
|
||||
function padRight(str, length) {
|
||||
return str.length >= length ? str : `${str}${" ".repeat(length - str.length)}`;
|
||||
}
|
||||
function camelcase(input) {
|
||||
return input.replaceAll(/([a-z])-([a-z])/g, (_, p1, p2) => {
|
||||
return p1 + p2.toUpperCase();
|
||||
});
|
||||
}
|
||||
function setDotProp(obj, keys, val) {
|
||||
let current = obj;
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
if (i === keys.length - 1) {
|
||||
current[key] = val;
|
||||
return;
|
||||
}
|
||||
if (current[key] == null) {
|
||||
const nextKeyIsArrayIndex = +keys[i + 1] > -1;
|
||||
current[key] = nextKeyIsArrayIndex ? [] : {};
|
||||
}
|
||||
current = current[key];
|
||||
}
|
||||
}
|
||||
function setByType(obj, transforms) {
|
||||
for (const key of Object.keys(transforms)) {
|
||||
const transform = transforms[key];
|
||||
if (transform.shouldTransform) {
|
||||
obj[key] = [obj[key]].flat();
|
||||
if (typeof transform.transformFunction === "function") obj[key] = obj[key].map(transform.transformFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
function getFileName(input) {
|
||||
const m = /([^\\/]+)$/.exec(input);
|
||||
return m ? m[1] : "";
|
||||
}
|
||||
function camelcaseOptionName(name) {
|
||||
return name.split(".").map((v, i) => {
|
||||
return i === 0 ? camelcase(v) : v;
|
||||
}).join(".");
|
||||
}
|
||||
var CACError = class extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = "CACError";
|
||||
if (typeof Error.captureStackTrace !== "function") this.stack = new Error(message).stack;
|
||||
}
|
||||
};
|
||||
var Option = class {
|
||||
rawName;
|
||||
description;
|
||||
/** Option name */
|
||||
name;
|
||||
/** Option name and aliases */
|
||||
names;
|
||||
isBoolean;
|
||||
required;
|
||||
config;
|
||||
negated;
|
||||
constructor(rawName, description, config) {
|
||||
this.rawName = rawName;
|
||||
this.description = description;
|
||||
this.config = Object.assign({}, config);
|
||||
rawName = rawName.replaceAll(".*", "");
|
||||
this.negated = false;
|
||||
this.names = removeBrackets(rawName).split(",").map((v) => {
|
||||
let name = v.trim().replace(/^-{1,2}/, "");
|
||||
if (name.startsWith("no-")) {
|
||||
this.negated = true;
|
||||
name = name.replace(/^no-/, "");
|
||||
}
|
||||
return camelcaseOptionName(name);
|
||||
}).sort((a, b) => a.length > b.length ? 1 : -1);
|
||||
this.name = this.names.at(-1);
|
||||
if (this.negated && this.config.default == null) this.config.default = true;
|
||||
if (rawName.includes("<")) this.required = true;
|
||||
else if (rawName.includes("[")) this.required = false;
|
||||
else this.isBoolean = true;
|
||||
}
|
||||
};
|
||||
let runtimeProcessArgs;
|
||||
let runtimeInfo;
|
||||
if (typeof process !== "undefined") {
|
||||
let runtimeName;
|
||||
if (typeof Deno !== "undefined" && typeof Deno.version?.deno === "string") runtimeName = "deno";
|
||||
else if (typeof Bun !== "undefined" && typeof Bun.version === "string") runtimeName = "bun";
|
||||
else runtimeName = "node";
|
||||
runtimeInfo = `${process.platform}-${process.arch} ${runtimeName}-${process.version}`;
|
||||
runtimeProcessArgs = process.argv;
|
||||
} else if (typeof navigator === "undefined") runtimeInfo = `unknown`;
|
||||
else runtimeInfo = `${navigator.platform} ${navigator.userAgent}`;
|
||||
var Command = class {
|
||||
rawName;
|
||||
description;
|
||||
config;
|
||||
cli;
|
||||
options;
|
||||
aliasNames;
|
||||
name;
|
||||
args;
|
||||
commandAction;
|
||||
usageText;
|
||||
versionNumber;
|
||||
examples;
|
||||
helpCallback;
|
||||
globalCommand;
|
||||
constructor(rawName, description, config = {}, cli) {
|
||||
this.rawName = rawName;
|
||||
this.description = description;
|
||||
this.config = config;
|
||||
this.cli = cli;
|
||||
this.options = [];
|
||||
this.aliasNames = [];
|
||||
this.name = removeBrackets(rawName);
|
||||
this.args = findAllBrackets(rawName);
|
||||
this.examples = [];
|
||||
}
|
||||
usage(text) {
|
||||
this.usageText = text;
|
||||
return this;
|
||||
}
|
||||
allowUnknownOptions() {
|
||||
this.config.allowUnknownOptions = true;
|
||||
return this;
|
||||
}
|
||||
ignoreOptionDefaultValue() {
|
||||
this.config.ignoreOptionDefaultValue = true;
|
||||
return this;
|
||||
}
|
||||
version(version, customFlags = "-v, --version") {
|
||||
this.versionNumber = version;
|
||||
this.option(customFlags, "Display version number");
|
||||
return this;
|
||||
}
|
||||
example(example) {
|
||||
this.examples.push(example);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Add a option for this command
|
||||
* @param rawName Raw option name(s)
|
||||
* @param description Option description
|
||||
* @param config Option config
|
||||
*/
|
||||
option(rawName, description, config) {
|
||||
const option = new Option(rawName, description, config);
|
||||
this.options.push(option);
|
||||
return this;
|
||||
}
|
||||
alias(name) {
|
||||
this.aliasNames.push(name);
|
||||
return this;
|
||||
}
|
||||
action(callback) {
|
||||
this.commandAction = callback;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Check if a command name is matched by this command
|
||||
* @param name Command name
|
||||
*/
|
||||
isMatched(name) {
|
||||
return this.name === name || this.aliasNames.includes(name);
|
||||
}
|
||||
get isDefaultCommand() {
|
||||
return this.name === "" || this.aliasNames.includes("!");
|
||||
}
|
||||
get isGlobalCommand() {
|
||||
return this instanceof GlobalCommand;
|
||||
}
|
||||
/**
|
||||
* Check if an option is registered in this command
|
||||
* @param name Option name
|
||||
*/
|
||||
hasOption(name) {
|
||||
name = name.split(".")[0];
|
||||
return this.options.find((option) => {
|
||||
return option.names.includes(name);
|
||||
});
|
||||
}
|
||||
outputHelp() {
|
||||
const { name, commands } = this.cli;
|
||||
const { versionNumber, options: globalOptions, helpCallback } = this.cli.globalCommand;
|
||||
let sections = [{ body: `${name}${versionNumber ? `/${versionNumber}` : ""}` }];
|
||||
sections.push({
|
||||
title: "Usage",
|
||||
body: ` $ ${name} ${this.usageText || this.rawName}`
|
||||
});
|
||||
if ((this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0) {
|
||||
const longestCommandName = findLongest(commands.map((command) => command.rawName));
|
||||
sections.push({
|
||||
title: "Commands",
|
||||
body: commands.map((command) => {
|
||||
return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
|
||||
}).join("\n")
|
||||
}, {
|
||||
title: `For more info, run any command with the \`--help\` flag`,
|
||||
body: commands.map((command) => ` $ ${name}${command.name === "" ? "" : ` ${command.name}`} --help`).join("\n")
|
||||
});
|
||||
}
|
||||
let options = this.isGlobalCommand ? globalOptions : [...this.options, ...globalOptions || []];
|
||||
if (!this.isGlobalCommand && !this.isDefaultCommand) options = options.filter((option) => option.name !== "version");
|
||||
if (options.length > 0) {
|
||||
const longestOptionName = findLongest(options.map((option) => option.rawName));
|
||||
sections.push({
|
||||
title: "Options",
|
||||
body: options.map((option) => {
|
||||
return ` ${padRight(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === void 0 ? "" : `(default: ${option.config.default})`}`;
|
||||
}).join("\n")
|
||||
});
|
||||
}
|
||||
if (this.examples.length > 0) sections.push({
|
||||
title: "Examples",
|
||||
body: this.examples.map((example) => {
|
||||
if (typeof example === "function") return example(name);
|
||||
return example;
|
||||
}).join("\n")
|
||||
});
|
||||
if (helpCallback) sections = helpCallback(sections) || sections;
|
||||
console.info(sections.map((section) => {
|
||||
return section.title ? `${section.title}:\n${section.body}` : section.body;
|
||||
}).join("\n\n"));
|
||||
}
|
||||
outputVersion() {
|
||||
const { name } = this.cli;
|
||||
const { versionNumber } = this.cli.globalCommand;
|
||||
if (versionNumber) console.info(`${name}/${versionNumber} ${runtimeInfo}`);
|
||||
}
|
||||
checkRequiredArgs() {
|
||||
const minimalArgsCount = this.args.filter((arg) => arg.required).length;
|
||||
if (this.cli.args.length < minimalArgsCount) throw new CACError(`missing required args for command \`${this.rawName}\``);
|
||||
}
|
||||
/**
|
||||
* Check if the parsed options contain any unknown options
|
||||
*
|
||||
* Exit and output error when true
|
||||
*/
|
||||
checkUnknownOptions() {
|
||||
const { options, globalCommand } = this.cli;
|
||||
if (!this.config.allowUnknownOptions) {
|
||||
for (const name of Object.keys(options)) if (name !== "--" && !this.hasOption(name) && !globalCommand.hasOption(name)) throw new CACError(`Unknown option \`${name.length > 1 ? `--${name}` : `-${name}`}\``);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Check if the required string-type options exist
|
||||
*/
|
||||
checkOptionValue() {
|
||||
const { options: parsedOptions, globalCommand } = this.cli;
|
||||
const options = [...globalCommand.options, ...this.options];
|
||||
for (const option of options) {
|
||||
const value = parsedOptions[option.name.split(".")[0]];
|
||||
if (option.required) {
|
||||
const hasNegated = options.some((o) => o.negated && o.names.includes(option.name));
|
||||
if (value === true || value === false && !hasNegated) throw new CACError(`option \`${option.rawName}\` value is missing`);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Check if the number of args is more than expected
|
||||
*/
|
||||
checkUnusedArgs() {
|
||||
const maximumArgsCount = this.args.some((arg) => arg.variadic) ? Infinity : this.args.length;
|
||||
if (maximumArgsCount < this.cli.args.length) throw new CACError(`Unused args: ${this.cli.args.slice(maximumArgsCount).map((arg) => `\`${arg}\``).join(", ")}`);
|
||||
}
|
||||
};
|
||||
var GlobalCommand = class extends Command {
|
||||
constructor(cli) {
|
||||
super("@@global@@", "", {}, cli);
|
||||
}
|
||||
};
|
||||
var CAC = class extends EventTarget {
|
||||
/** The program name to display in help and version message */
|
||||
name;
|
||||
commands;
|
||||
globalCommand;
|
||||
matchedCommand;
|
||||
matchedCommandName;
|
||||
/**
|
||||
* Raw CLI arguments
|
||||
*/
|
||||
rawArgs;
|
||||
/**
|
||||
* Parsed CLI arguments
|
||||
*/
|
||||
args;
|
||||
/**
|
||||
* Parsed CLI options, camelCased
|
||||
*/
|
||||
options;
|
||||
showHelpOnExit;
|
||||
showVersionOnExit;
|
||||
/**
|
||||
* @param name The program name to display in help and version message
|
||||
*/
|
||||
constructor(name = "") {
|
||||
super();
|
||||
this.name = name;
|
||||
this.commands = [];
|
||||
this.rawArgs = [];
|
||||
this.args = [];
|
||||
this.options = {};
|
||||
this.globalCommand = new GlobalCommand(this);
|
||||
this.globalCommand.usage("<command> [options]");
|
||||
}
|
||||
/**
|
||||
* Add a global usage text.
|
||||
*
|
||||
* This is not used by sub-commands.
|
||||
*/
|
||||
usage(text) {
|
||||
this.globalCommand.usage(text);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Add a sub-command
|
||||
*/
|
||||
command(rawName, description, config) {
|
||||
const command = new Command(rawName, description || "", config, this);
|
||||
command.globalCommand = this.globalCommand;
|
||||
this.commands.push(command);
|
||||
return command;
|
||||
}
|
||||
/**
|
||||
* Add a global CLI option.
|
||||
*
|
||||
* Which is also applied to sub-commands.
|
||||
*/
|
||||
option(rawName, description, config) {
|
||||
this.globalCommand.option(rawName, description, config);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Show help message when `-h, --help` flags appear.
|
||||
*
|
||||
*/
|
||||
help(callback) {
|
||||
this.globalCommand.option("-h, --help", "Display this message");
|
||||
this.globalCommand.helpCallback = callback;
|
||||
this.showHelpOnExit = true;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Show version number when `-v, --version` flags appear.
|
||||
*
|
||||
*/
|
||||
version(version, customFlags = "-v, --version") {
|
||||
this.globalCommand.version(version, customFlags);
|
||||
this.showVersionOnExit = true;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Add a global example.
|
||||
*
|
||||
* This example added here will not be used by sub-commands.
|
||||
*/
|
||||
example(example) {
|
||||
this.globalCommand.example(example);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Output the corresponding help message
|
||||
* When a sub-command is matched, output the help message for the command
|
||||
* Otherwise output the global one.
|
||||
*
|
||||
*/
|
||||
outputHelp() {
|
||||
if (this.matchedCommand) this.matchedCommand.outputHelp();
|
||||
else this.globalCommand.outputHelp();
|
||||
}
|
||||
/**
|
||||
* Output the version number.
|
||||
*
|
||||
*/
|
||||
outputVersion() {
|
||||
this.globalCommand.outputVersion();
|
||||
}
|
||||
setParsedInfo({ args, options }, matchedCommand, matchedCommandName) {
|
||||
this.args = args;
|
||||
this.options = options;
|
||||
if (matchedCommand) this.matchedCommand = matchedCommand;
|
||||
if (matchedCommandName) this.matchedCommandName = matchedCommandName;
|
||||
return this;
|
||||
}
|
||||
unsetMatchedCommand() {
|
||||
this.matchedCommand = void 0;
|
||||
this.matchedCommandName = void 0;
|
||||
}
|
||||
/**
|
||||
* Parse argv
|
||||
*/
|
||||
parse(argv, { run = true } = {}) {
|
||||
if (!argv) {
|
||||
if (!runtimeProcessArgs) throw new Error("No argv provided and runtime process argv is not available.");
|
||||
argv = runtimeProcessArgs;
|
||||
}
|
||||
this.rawArgs = argv;
|
||||
if (!this.name) this.name = argv[1] ? getFileName(argv[1]) : "cli";
|
||||
let shouldParse = true;
|
||||
for (const command of this.commands) {
|
||||
const parsed = this.mri(argv.slice(2), command);
|
||||
const commandName = parsed.args[0];
|
||||
if (command.isMatched(commandName)) {
|
||||
shouldParse = false;
|
||||
const parsedInfo = {
|
||||
...parsed,
|
||||
args: parsed.args.slice(1)
|
||||
};
|
||||
this.setParsedInfo(parsedInfo, command, commandName);
|
||||
this.dispatchEvent(new CustomEvent(`command:${commandName}`, { detail: command }));
|
||||
}
|
||||
}
|
||||
if (shouldParse) {
|
||||
for (const command of this.commands) if (command.isDefaultCommand) {
|
||||
shouldParse = false;
|
||||
const parsed = this.mri(argv.slice(2), command);
|
||||
this.setParsedInfo(parsed, command);
|
||||
this.dispatchEvent(new CustomEvent("command:!", { detail: command }));
|
||||
}
|
||||
}
|
||||
if (shouldParse) {
|
||||
const parsed = this.mri(argv.slice(2));
|
||||
this.setParsedInfo(parsed);
|
||||
}
|
||||
if (this.options.help && this.showHelpOnExit) {
|
||||
this.outputHelp();
|
||||
run = false;
|
||||
this.unsetMatchedCommand();
|
||||
}
|
||||
if (this.options.version && this.showVersionOnExit && this.matchedCommandName == null) {
|
||||
this.outputVersion();
|
||||
run = false;
|
||||
this.unsetMatchedCommand();
|
||||
}
|
||||
const parsedArgv = {
|
||||
args: this.args,
|
||||
options: this.options
|
||||
};
|
||||
if (run) this.runMatchedCommand();
|
||||
if (!this.matchedCommand && this.args[0]) this.dispatchEvent(new CustomEvent("command:*", { detail: this.args[0] }));
|
||||
return parsedArgv;
|
||||
}
|
||||
mri(argv, command) {
|
||||
const cliOptions = [...this.globalCommand.options, ...command ? command.options : []];
|
||||
const mriOptions = getMriOptions(cliOptions);
|
||||
let argsAfterDoubleDashes = [];
|
||||
const doubleDashesIndex = argv.indexOf("--");
|
||||
if (doubleDashesIndex !== -1) {
|
||||
argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);
|
||||
argv = argv.slice(0, doubleDashesIndex);
|
||||
}
|
||||
let parsed = lib_default(argv, mriOptions);
|
||||
parsed = Object.keys(parsed).reduce((res, name) => {
|
||||
return {
|
||||
...res,
|
||||
[camelcaseOptionName(name)]: parsed[name]
|
||||
};
|
||||
}, { _: [] });
|
||||
const args = parsed._;
|
||||
const options = { "--": argsAfterDoubleDashes };
|
||||
const ignoreDefault = command && command.config.ignoreOptionDefaultValue ? command.config.ignoreOptionDefaultValue : this.globalCommand.config.ignoreOptionDefaultValue;
|
||||
const transforms = Object.create(null);
|
||||
for (const cliOption of cliOptions) {
|
||||
if (!ignoreDefault && cliOption.config.default !== void 0) for (const name of cliOption.names) options[name] = cliOption.config.default;
|
||||
if (Array.isArray(cliOption.config.type) && transforms[cliOption.name] === void 0) {
|
||||
transforms[cliOption.name] = Object.create(null);
|
||||
transforms[cliOption.name].shouldTransform = true;
|
||||
transforms[cliOption.name].transformFunction = cliOption.config.type[0];
|
||||
}
|
||||
}
|
||||
for (const key of Object.keys(parsed)) if (key !== "_") {
|
||||
setDotProp(options, key.split("."), parsed[key]);
|
||||
setByType(options, transforms);
|
||||
}
|
||||
return {
|
||||
args,
|
||||
options
|
||||
};
|
||||
}
|
||||
runMatchedCommand() {
|
||||
const { args, options, matchedCommand: command } = this;
|
||||
if (!command || !command.commandAction) return;
|
||||
command.checkUnknownOptions();
|
||||
command.checkOptionValue();
|
||||
command.checkRequiredArgs();
|
||||
command.checkUnusedArgs();
|
||||
const actionArgs = [];
|
||||
command.args.forEach((arg, index) => {
|
||||
if (arg.variadic) actionArgs.push(args.slice(index));
|
||||
else actionArgs.push(args[index]);
|
||||
});
|
||||
actionArgs.push(options);
|
||||
return command.commandAction.apply(this, actionArgs);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @param name The program name to display in help and version message
|
||||
*/
|
||||
const cac = (name = "") => new CAC(name);
|
||||
//#endregion
|
||||
//#region src/node/cli.ts
|
||||
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
||||
function checkNodeVersion(nodeVersion) {
|
||||
const currentVersion = nodeVersion.split(".");
|
||||
const major = parseInt(currentVersion[0], 10);
|
||||
const minor = parseInt(currentVersion[1], 10);
|
||||
return major === 20 && minor >= 19 || major === 22 && minor >= 12 || major > 22;
|
||||
}
|
||||
if (!checkNodeVersion(process.versions.node)) console.warn(import_picocolors.default.yellow(`You are using Node.js ${process.versions.node}. Vite requires Node.js version 20.19+ or 22.12+. Please upgrade your Node.js version.`));
|
||||
const cli = cac("vite");
|
||||
let profileSession = global.__vite_profile_session;
|
||||
let profileCount = 0;
|
||||
const stopProfiler = (log) => {
|
||||
if (!profileSession) return;
|
||||
return new Promise((res, rej) => {
|
||||
profileSession.post("Profiler.stop", (err, { profile }) => {
|
||||
if (!err) {
|
||||
const outPath = path.resolve(`./vite-profile-${profileCount++}.cpuprofile`);
|
||||
fs.writeFileSync(outPath, JSON.stringify(profile));
|
||||
log(import_picocolors.default.yellow(`CPU profile written to ${import_picocolors.default.white(import_picocolors.default.dim(outPath))}`));
|
||||
profileSession = void 0;
|
||||
res();
|
||||
} else rej(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
const filterDuplicateOptions = (options) => {
|
||||
for (const [key, value] of Object.entries(options)) if (Array.isArray(value)) options[key] = value[value.length - 1];
|
||||
};
|
||||
/**
|
||||
* removing global flags before passing as command specific sub-configs
|
||||
*/
|
||||
function cleanGlobalCLIOptions(options) {
|
||||
const ret = { ...options };
|
||||
delete ret["--"];
|
||||
delete ret.c;
|
||||
delete ret.config;
|
||||
delete ret.base;
|
||||
delete ret.l;
|
||||
delete ret.logLevel;
|
||||
delete ret.clearScreen;
|
||||
delete ret.configLoader;
|
||||
delete ret.d;
|
||||
delete ret.debug;
|
||||
delete ret.f;
|
||||
delete ret.filter;
|
||||
delete ret.m;
|
||||
delete ret.mode;
|
||||
delete ret.force;
|
||||
delete ret.w;
|
||||
if ("sourcemap" in ret) {
|
||||
const sourcemap = ret.sourcemap;
|
||||
ret.sourcemap = sourcemap === "true" ? true : sourcemap === "false" ? false : ret.sourcemap;
|
||||
}
|
||||
if ("watch" in ret) ret.watch = ret.watch ? {} : void 0;
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* removing builder flags before passing as command specific sub-configs
|
||||
*/
|
||||
function cleanBuilderCLIOptions(options) {
|
||||
const ret = { ...options };
|
||||
delete ret.app;
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* host may be a number (like 0), should convert to string
|
||||
*/
|
||||
const convertHost = (v) => {
|
||||
if (typeof v === "number") return String(v);
|
||||
return v;
|
||||
};
|
||||
/**
|
||||
* base may be a number (like 0), should convert to empty string
|
||||
*/
|
||||
const convertBase = (v) => {
|
||||
if (v === 0) return "";
|
||||
return v;
|
||||
};
|
||||
cli.option("-c, --config <file>", `[string] use specified config file`).option("--base <path>", `[string] public base path (default: /)`, { type: [convertBase] }).option("-l, --logLevel <level>", `[string] info | warn | error | silent`).option("--clearScreen", `[boolean] allow/disable clear screen when logging`).option("--configLoader <loader>", `[string] use 'bundle' to bundle the config with Rolldown, or 'runner' (experimental) to process it on the fly, or 'native' (experimental) to load using the native runtime (default: bundle)`).option("-d, --debug [feat]", `[string | boolean] show debug logs`).option("-f, --filter <filter>", `[string] filter debug logs`).option("-m, --mode <mode>", `[string] set env mode`);
|
||||
cli.command("[root]", "start dev server").alias("serve").alias("dev").option("--host [host]", `[string] specify hostname`, { type: [convertHost] }).option("--port <port>", `[number] specify port`).option("--open [path]", `[boolean | string] open browser on startup`).option("--cors", `[boolean] enable CORS`).option("--strictPort", `[boolean] exit if specified port is already in use`).option("--force", `[boolean] force the optimizer to ignore the cache and re-bundle`).option("--experimentalBundle", `[boolean] use experimental full bundle mode (this is highly experimental)`).action(async (root, options) => {
|
||||
filterDuplicateOptions(options);
|
||||
const { createServer } = await import("./chunks/node.js").then((n) => n.j);
|
||||
try {
|
||||
const server = await createServer({
|
||||
root,
|
||||
base: options.base,
|
||||
mode: options.mode,
|
||||
configFile: options.config,
|
||||
configLoader: options.configLoader,
|
||||
logLevel: options.logLevel,
|
||||
clearScreen: options.clearScreen,
|
||||
server: cleanGlobalCLIOptions(options),
|
||||
forceOptimizeDeps: options.force,
|
||||
experimental: { bundledDev: options.experimentalBundle }
|
||||
});
|
||||
if (!server.httpServer) throw new Error("HTTP server not available");
|
||||
await server.listen();
|
||||
const info = server.config.logger.info;
|
||||
const modeString = options.mode && options.mode !== "development" ? ` ${import_picocolors.default.bgGreen(` ${import_picocolors.default.bold(options.mode)} `)}` : "";
|
||||
const viteStartTime = global.__vite_start_time ?? false;
|
||||
const startupDurationString = viteStartTime ? import_picocolors.default.dim(`ready in ${import_picocolors.default.reset(import_picocolors.default.bold(Math.ceil(performance.now() - viteStartTime)))} ms`) : "";
|
||||
const hasExistingLogs = process.stdout.bytesWritten > 0 || process.stderr.bytesWritten > 0;
|
||||
info(`\n ${import_picocolors.default.green(`${import_picocolors.default.bold("VITE")} v${VERSION}`)}${modeString} ${startupDurationString}\n`, { clear: !hasExistingLogs });
|
||||
server.printUrls();
|
||||
const customShortcuts = [];
|
||||
if (profileSession) customShortcuts.push({
|
||||
key: "p",
|
||||
description: "start/stop the profiler",
|
||||
async action(server) {
|
||||
if (profileSession) await stopProfiler(server.config.logger.info);
|
||||
else {
|
||||
const inspector = await import("node:inspector").then((r) => r.default);
|
||||
await new Promise((res) => {
|
||||
profileSession = new inspector.Session();
|
||||
profileSession.connect();
|
||||
profileSession.post("Profiler.enable", () => {
|
||||
profileSession.post("Profiler.start", () => {
|
||||
server.config.logger.info("Profiler started");
|
||||
res();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
server.bindCLIShortcuts({
|
||||
print: true,
|
||||
customShortcuts
|
||||
});
|
||||
} catch (e) {
|
||||
const logger = createLogger(options.logLevel);
|
||||
logger.error(import_picocolors.default.red(`error when starting dev server:\n${inspect(e)}`), { error: e });
|
||||
await stopProfiler(logger.info);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
cli.command("build [root]", "build for production").option("--target <target>", `[string] transpile target (default: 'baseline-widely-available')`).option("--outDir <dir>", `[string] output directory (default: dist)`).option("--assetsDir <dir>", `[string] directory under outDir to place assets in (default: assets)`).option("--assetsInlineLimit <number>", `[number] static asset base64 inline threshold in bytes (default: 4096)`).option("--ssr [entry]", `[string] build specified entry for server-side rendering`).option("--sourcemap [output]", `[boolean | "inline" | "hidden"] output source maps for build (default: false)`).option("--minify [minifier]", "[boolean | \"oxc\" | \"terser\" | \"esbuild\"] enable/disable minification, or specify minifier to use (default: oxc)").option("--manifest [name]", `[boolean | string] emit build manifest json`).option("--ssrManifest [name]", `[boolean | string] emit ssr manifest json`).option("--emptyOutDir", `[boolean] force empty outDir when it's outside of root`).option("-w, --watch", `[boolean] rebuilds when modules have changed on disk`).option("--app", `[boolean] same as \`builder: {}\``).action(async (root, options) => {
|
||||
filterDuplicateOptions(options);
|
||||
const buildOptions = cleanGlobalCLIOptions(cleanBuilderCLIOptions(options));
|
||||
try {
|
||||
const builder = await createBuilder({
|
||||
root,
|
||||
base: options.base,
|
||||
mode: options.mode,
|
||||
configFile: options.config,
|
||||
configLoader: options.configLoader,
|
||||
logLevel: options.logLevel,
|
||||
clearScreen: options.clearScreen,
|
||||
build: buildOptions,
|
||||
...options.app ? { builder: {} } : {}
|
||||
}, null);
|
||||
await builder.buildApp();
|
||||
await builder.runDevTools();
|
||||
} catch (e) {
|
||||
createLogger(options.logLevel).error(import_picocolors.default.red(`error during build:\n${inspect(e)}`), { error: e });
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await stopProfiler((message) => createLogger(options.logLevel).info(message));
|
||||
}
|
||||
});
|
||||
cli.command("optimize [root]", "pre-bundle dependencies (deprecated, the pre-bundle process runs automatically and does not need to be called)").option("--force", `[boolean] force the optimizer to ignore the cache and re-bundle`).action(async (root, options) => {
|
||||
filterDuplicateOptions(options);
|
||||
const { resolveConfig } = await import("./chunks/node.js").then((n) => n.f);
|
||||
const { optimizeDeps } = await import("./chunks/node.js").then((n) => n.O);
|
||||
try {
|
||||
await optimizeDeps(await resolveConfig({
|
||||
root,
|
||||
base: options.base,
|
||||
configFile: options.config,
|
||||
configLoader: options.configLoader,
|
||||
logLevel: options.logLevel,
|
||||
mode: options.mode
|
||||
}, "serve"), options.force, true);
|
||||
} catch (e) {
|
||||
createLogger(options.logLevel).error(import_picocolors.default.red(`error when optimizing deps:\n${inspect(e)}`), { error: e });
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
cli.command("preview [root]", "locally preview production build").option("--host [host]", `[string] specify hostname`, { type: [convertHost] }).option("--port <port>", `[number] specify port`).option("--strictPort", `[boolean] exit if specified port is already in use`).option("--open [path]", `[boolean | string] open browser on startup`).option("--outDir <dir>", `[string] output directory (default: dist)`).action(async (root, options) => {
|
||||
filterDuplicateOptions(options);
|
||||
const { preview } = await import("./chunks/node.js").then((n) => n.y);
|
||||
try {
|
||||
const server = await preview({
|
||||
root,
|
||||
base: options.base,
|
||||
configFile: options.config,
|
||||
configLoader: options.configLoader,
|
||||
logLevel: options.logLevel,
|
||||
mode: options.mode,
|
||||
build: { outDir: options.outDir },
|
||||
preview: {
|
||||
port: options.port,
|
||||
strictPort: options.strictPort,
|
||||
host: options.host,
|
||||
open: options.open
|
||||
}
|
||||
});
|
||||
server.printUrls();
|
||||
server.bindCLIShortcuts({ print: true });
|
||||
} catch (e) {
|
||||
createLogger(options.logLevel).error(import_picocolors.default.red(`error when starting preview server:\n${inspect(e)}`), { error: e });
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await stopProfiler((message) => createLogger(options.logLevel).info(message));
|
||||
}
|
||||
});
|
||||
cli.help();
|
||||
cli.version(VERSION);
|
||||
cli.parse();
|
||||
//#endregion
|
||||
export { stopProfiler };
|
||||
@@ -0,0 +1,76 @@
|
||||
"use strict";
|
||||
|
||||
var _overload_yield = require("./_overload_yield.cjs");
|
||||
|
||||
function _async_generator(gen) {
|
||||
var front, back;
|
||||
|
||||
function send(key, arg) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var request = { key: key, arg: arg, resolve: resolve, reject: reject, next: null };
|
||||
if (back) back = back.next = request;
|
||||
else {
|
||||
front = back = request;
|
||||
resume(key, arg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resume(key, arg) {
|
||||
try {
|
||||
var result = gen[key](arg);
|
||||
var value = result.value;
|
||||
var overloaded = value instanceof _overload_yield._;
|
||||
Promise.resolve(overloaded ? value.v : value).then(function(arg) {
|
||||
if (overloaded) {
|
||||
var nextKey = key === "return" ? "return" : "next";
|
||||
if (!value.k || arg.done) return resume(nextKey, arg);
|
||||
else arg = gen[nextKey](arg).value;
|
||||
}
|
||||
settle(result.done ? "return" : "normal", arg);
|
||||
}, function(err) {
|
||||
resume("throw", err);
|
||||
});
|
||||
} catch (err) {
|
||||
settle("throw", err);
|
||||
}
|
||||
}
|
||||
|
||||
function settle(type, value) {
|
||||
switch (type) {
|
||||
case "return":
|
||||
front.resolve({ value: value, done: true });
|
||||
break;
|
||||
case "throw":
|
||||
front.reject(value);
|
||||
break;
|
||||
default:
|
||||
front.resolve({ value: value, done: false });
|
||||
break;
|
||||
}
|
||||
front = front.next;
|
||||
if (front) resume(front.key, front.arg);
|
||||
else back = null;
|
||||
}
|
||||
|
||||
this._invoke = send;
|
||||
|
||||
if (typeof gen.return !== "function") this.return = undefined;
|
||||
}
|
||||
|
||||
_async_generator.prototype[(typeof Symbol === "function" && Symbol.asyncIterator) || "@@asyncIterator"] = function() {
|
||||
return this;
|
||||
};
|
||||
|
||||
_async_generator.prototype.next = function(arg) {
|
||||
return this._invoke("next", arg);
|
||||
};
|
||||
|
||||
_async_generator.prototype.throw = function(arg) {
|
||||
return this._invoke("throw", arg);
|
||||
};
|
||||
|
||||
_async_generator.prototype.return = function(arg) {
|
||||
return this._invoke("return", arg);
|
||||
};
|
||||
exports._ = _async_generator;
|
||||
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* @fileoverview Utility to get information about the execution environment.
|
||||
* @author Kai Cataldo
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const path = require("node:path");
|
||||
const spawn = require("cross-spawn");
|
||||
const os = require("node:os");
|
||||
const log = require("../shared/logging");
|
||||
const packageJson = require("../../package.json");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates and returns execution environment information.
|
||||
* @returns {string} A string that contains execution environment information.
|
||||
*/
|
||||
function environment() {
|
||||
const cache = new Map();
|
||||
|
||||
/**
|
||||
* Checks if a path is a child of a directory.
|
||||
* @param {string} parentPath The parent path to check.
|
||||
* @param {string} childPath The path to check.
|
||||
* @returns {boolean} Whether or not the given path is a child of a directory.
|
||||
*/
|
||||
function isChildOfDirectory(parentPath, childPath) {
|
||||
return !path.relative(parentPath, childPath).startsWith("..");
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously executes a shell command and formats the result.
|
||||
* @param {string} cmd The command to execute.
|
||||
* @param {Array} args The arguments to be executed with the command.
|
||||
* @throws {Error} As may be collected by `cross-spawn.sync`.
|
||||
* @returns {string} The version returned by the command.
|
||||
*/
|
||||
function execCommand(cmd, args) {
|
||||
const key = [cmd, ...args].join(" ");
|
||||
|
||||
if (cache.has(key)) {
|
||||
return cache.get(key);
|
||||
}
|
||||
|
||||
const process = spawn.sync(cmd, args, { encoding: "utf8" });
|
||||
|
||||
if (process.error) {
|
||||
throw process.error;
|
||||
}
|
||||
|
||||
const result = process.stdout.trim();
|
||||
|
||||
cache.set(key, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a version number.
|
||||
* @param {string} versionStr The string to normalize.
|
||||
* @returns {string} The normalized version number.
|
||||
*/
|
||||
function normalizeVersionStr(versionStr) {
|
||||
return versionStr.startsWith("v") ? versionStr : `v${versionStr}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets bin version.
|
||||
* @param {string} bin The bin to check.
|
||||
* @throws {Error} As may be collected by `cross-spawn.sync`.
|
||||
* @returns {string} The normalized version returned by the command.
|
||||
*/
|
||||
function getBinVersion(bin) {
|
||||
const binArgs = ["--version"];
|
||||
|
||||
try {
|
||||
return normalizeVersionStr(execCommand(bin, binArgs));
|
||||
} catch (e) {
|
||||
log.error(
|
||||
`Error finding ${bin} version running the command \`${bin} ${binArgs.join(" ")}\``,
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets installed npm package version.
|
||||
* @param {string} pkg The package to check.
|
||||
* @param {boolean} global Whether to check globally or not.
|
||||
* @throws {Error} As may be collected by `cross-spawn.sync`.
|
||||
* @returns {string} The normalized version returned by the command.
|
||||
*/
|
||||
function getNpmPackageVersion(pkg, { global = false } = {}) {
|
||||
const npmBinArgs = ["bin", "-g"];
|
||||
const npmLsArgs = ["ls", "--depth=0", "--json", pkg];
|
||||
|
||||
if (global) {
|
||||
npmLsArgs.push("-g");
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedStdout = JSON.parse(execCommand("npm", npmLsArgs));
|
||||
|
||||
/*
|
||||
* Checking globally returns an empty JSON object, while local checks
|
||||
* include the name and version of the local project.
|
||||
*/
|
||||
if (
|
||||
Object.keys(parsedStdout).length === 0 ||
|
||||
!(parsedStdout.dependencies && parsedStdout.dependencies.eslint)
|
||||
) {
|
||||
return "Not found";
|
||||
}
|
||||
|
||||
const [, processBinPath] = process.argv;
|
||||
let npmBinPath;
|
||||
|
||||
try {
|
||||
npmBinPath = execCommand("npm", npmBinArgs);
|
||||
} catch (e) {
|
||||
log.error(
|
||||
`Error finding npm binary path when running command \`npm ${npmBinArgs.join(" ")}\``,
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
|
||||
const isGlobal = isChildOfDirectory(npmBinPath, processBinPath);
|
||||
let pkgVersion = parsedStdout.dependencies.eslint.version;
|
||||
|
||||
if ((global && isGlobal) || (!global && !isGlobal)) {
|
||||
pkgVersion += " (Currently used)";
|
||||
}
|
||||
|
||||
return normalizeVersionStr(pkgVersion);
|
||||
} catch (e) {
|
||||
log.error(
|
||||
`Error finding ${pkg} version running the command \`npm ${npmLsArgs.join(" ")}\``,
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
"Environment Info:",
|
||||
"",
|
||||
`Node version: ${process.version}`,
|
||||
`npm version: ${getBinVersion("npm")}`,
|
||||
`Local ESLint version: ${getNpmPackageVersion("eslint", { global: false })}`,
|
||||
`Global ESLint version: ${getNpmPackageVersion("eslint", { global: true })}`,
|
||||
`Operating System: ${os.platform()} ${os.release()}`,
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns version of currently executing ESLint.
|
||||
* @returns {string} The version from the currently executing ESLint's package.json.
|
||||
*/
|
||||
function version() {
|
||||
return `v${packageJson.version}`;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
__esModule: true, // Indicate intent for imports, remove ambiguity for Knip (see: https://github.com/eslint/eslint/pull/18005#discussion_r1484422616)
|
||||
environment,
|
||||
version,
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
|
||||
/// <reference no-default-lib="true"/>
|
||||
|
||||
interface String {
|
||||
/**
|
||||
* Replace all instances of a substring in a string, using a regular expression or search string.
|
||||
* @param searchValue A string to search for.
|
||||
* @param replaceValue A string containing the text to replace for every successful match of searchValue in this string.
|
||||
*/
|
||||
replaceAll(searchValue: string | RegExp, replaceValue: string): string;
|
||||
|
||||
/**
|
||||
* Replace all instances of a substring in a string, using a regular expression or search string.
|
||||
* @param searchValue A string to search for.
|
||||
* @param replacer A function that returns the replacement text.
|
||||
*/
|
||||
replaceAll(searchValue: string | RegExp, replacer: (substring: string, ...args: any[]) => string): string;
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const SonicBoom = require('../')
|
||||
const { file, runTests } = require('./helper')
|
||||
|
||||
const isWindows = process.platform === 'win32'
|
||||
|
||||
runTests(buildTests)
|
||||
|
||||
function buildTests (test, sync) {
|
||||
// Reset the umask for testing
|
||||
process.umask(0o000)
|
||||
|
||||
test('mode', { skip: isWindows }, (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const dest = file()
|
||||
const mode = 0o666
|
||||
const stream = new SonicBoom({ dest, sync, mode })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
t.equal(fs.statSync(dest).mode & 0o777, stream.mode)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('mode default', { skip: isWindows }, (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const dest = file()
|
||||
const defaultMode = 0o666
|
||||
const stream = new SonicBoom({ dest, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
t.equal(fs.statSync(dest).mode & 0o777, defaultMode)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('mode on mkdir', { skip: isWindows }, (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const dest = path.join(file(), 'out.log')
|
||||
const mode = 0o666
|
||||
const stream = new SonicBoom({ dest, mkdir: true, mode, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
|
||||
stream.flush()
|
||||
|
||||
stream.on('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\n')
|
||||
t.equal(fs.statSync(dest).mode & 0o777, stream.mode)
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('mode on append', { skip: isWindows }, (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const dest = file()
|
||||
fs.writeFileSync(dest, 'hello world\n', 'utf8', 0o422)
|
||||
const mode = isWindows ? 0o444 : 0o666
|
||||
const stream = new SonicBoom({ dest, append: false, mode, sync })
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.flush()
|
||||
|
||||
stream.on('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'something else\n')
|
||||
t.equal(fs.statSync(dest).mode & 0o777, stream.mode)
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('node:test')
|
||||
const colors = require('../colors')
|
||||
const prettifyObject = require('./prettify-object')
|
||||
const {
|
||||
ERROR_LIKE_KEYS
|
||||
} = require('../constants')
|
||||
|
||||
const context = {
|
||||
EOL: '\n',
|
||||
IDENT: ' ',
|
||||
customPrettifiers: {},
|
||||
errorLikeObjectKeys: ERROR_LIKE_KEYS,
|
||||
objectColorizer: colors(),
|
||||
singleLine: false,
|
||||
colorizer: colors()
|
||||
}
|
||||
|
||||
test('returns empty string if no properties present', t => {
|
||||
const str = prettifyObject({ log: {}, context })
|
||||
t.assert.strictEqual(str, '')
|
||||
})
|
||||
|
||||
test('works with single level properties', t => {
|
||||
const str = prettifyObject({ log: { foo: 'bar' }, context })
|
||||
t.assert.strictEqual(str, ' foo: "bar"\n')
|
||||
})
|
||||
|
||||
test('works with multiple level properties', t => {
|
||||
const str = prettifyObject({ log: { foo: { bar: 'baz' } }, context })
|
||||
t.assert.strictEqual(str, ' foo: {\n "bar": "baz"\n }\n')
|
||||
})
|
||||
|
||||
test('skips specified keys', t => {
|
||||
const str = prettifyObject({
|
||||
log: { foo: 'bar', hello: 'world' },
|
||||
skipKeys: ['foo'],
|
||||
context
|
||||
})
|
||||
t.assert.strictEqual(str, ' hello: "world"\n')
|
||||
})
|
||||
|
||||
test('ignores predefined keys', t => {
|
||||
const str = prettifyObject({ log: { foo: 'bar', pid: 12345 }, context })
|
||||
t.assert.strictEqual(str, ' foo: "bar"\n')
|
||||
})
|
||||
|
||||
test('ignores escaped backslashes in string values', t => {
|
||||
const str = prettifyObject({ log: { foo_regexp: '\\[^\\w\\s]\\' }, context })
|
||||
t.assert.strictEqual(str, ' foo_regexp: "\\[^\\w\\s]\\"\n')
|
||||
})
|
||||
|
||||
test('ignores escaped backslashes in string values (singleLine option)', t => {
|
||||
const str = prettifyObject({
|
||||
log: { foo_regexp: '\\[^\\w\\s]\\' },
|
||||
context: {
|
||||
...context,
|
||||
singleLine: true
|
||||
}
|
||||
})
|
||||
t.assert.strictEqual(str, '{"foo_regexp":"\\[^\\w\\s]\\"}\n')
|
||||
})
|
||||
|
||||
test('works with error props', t => {
|
||||
const err = Error('Something went wrong')
|
||||
const serializedError = {
|
||||
message: err.message,
|
||||
stack: err.stack
|
||||
}
|
||||
const str = prettifyObject({ log: { error: serializedError }, context })
|
||||
t.assert.ok(str.startsWith(' error:'))
|
||||
t.assert.ok(str.includes(' "message": "Something went wrong",'))
|
||||
t.assert.ok(str.includes(' Error: Something went wrong'))
|
||||
})
|
||||
|
||||
test('customPrettifiers gets applied', t => {
|
||||
const customPrettifiers = {
|
||||
foo: v => v.toUpperCase()
|
||||
}
|
||||
const str = prettifyObject({
|
||||
log: { foo: 'foo' },
|
||||
context: {
|
||||
...context,
|
||||
customPrettifiers
|
||||
}
|
||||
})
|
||||
t.assert.strictEqual(str.startsWith(' foo: FOO'), true)
|
||||
})
|
||||
|
||||
test('skips lines omitted by customPrettifiers', t => {
|
||||
const customPrettifiers = {
|
||||
foo: () => { return undefined }
|
||||
}
|
||||
const str = prettifyObject({
|
||||
log: { foo: 'foo', bar: 'bar' },
|
||||
context: {
|
||||
...context,
|
||||
customPrettifiers
|
||||
}
|
||||
})
|
||||
t.assert.strictEqual(str.includes('bar: "bar"'), true)
|
||||
t.assert.strictEqual(str.includes('foo: "foo"'), false)
|
||||
})
|
||||
|
||||
test('joined lines omits starting eol', t => {
|
||||
const str = prettifyObject({
|
||||
log: { msg: 'doing work', calls: ['step 1', 'step 2', 'step 3'], level: 30 },
|
||||
context: {
|
||||
...context,
|
||||
IDENT: '',
|
||||
customPrettifiers: {
|
||||
calls: val => '\n' + val.map(it => ' ' + it).join('\n')
|
||||
}
|
||||
}
|
||||
})
|
||||
t.assert.strictEqual(str, [
|
||||
'msg: "doing work"',
|
||||
'calls:',
|
||||
' step 1',
|
||||
' step 2',
|
||||
' step 3',
|
||||
''
|
||||
].join('\n'))
|
||||
})
|
||||
|
||||
test('errors skips prettifiers', t => {
|
||||
const customPrettifiers = {
|
||||
err: () => { return 'is_err' }
|
||||
}
|
||||
const str = prettifyObject({
|
||||
log: { err: Error('boom') },
|
||||
context: {
|
||||
...context,
|
||||
customPrettifiers
|
||||
}
|
||||
})
|
||||
t.assert.strictEqual(str.includes('err: is_err'), true)
|
||||
})
|
||||
|
||||
test('errors skips prettifying if no lines are present', t => {
|
||||
const customPrettifiers = {
|
||||
err: () => { return undefined }
|
||||
}
|
||||
const str = prettifyObject({
|
||||
log: { err: Error('boom') },
|
||||
context: {
|
||||
...context,
|
||||
customPrettifiers
|
||||
}
|
||||
})
|
||||
t.assert.strictEqual(str, '')
|
||||
})
|
||||
|
||||
test('works with single level properties', t => {
|
||||
const colorizer = colors(true)
|
||||
const str = prettifyObject({
|
||||
log: { foo: 'bar' },
|
||||
context: {
|
||||
...context,
|
||||
objectColorizer: colorizer,
|
||||
colorizer
|
||||
}
|
||||
})
|
||||
t.assert.strictEqual(str, ` ${colorizer.colors.magenta('foo')}: "bar"\n`)
|
||||
})
|
||||
|
||||
test('works with customColors', t => {
|
||||
const colorizer = colors(true, [])
|
||||
t.assert.doesNotThrow(() => {
|
||||
prettifyObject({
|
||||
log: { foo: 'bar' },
|
||||
context: {
|
||||
...context,
|
||||
objectColorizer: colorizer,
|
||||
colorizer
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('customColors gets applied', t => {
|
||||
const colorizer = colors(true, [['property', 'green']])
|
||||
const str = prettifyObject({
|
||||
log: { foo: 'bar' },
|
||||
context: {
|
||||
...context,
|
||||
objectColorizer: colorizer,
|
||||
colorizer
|
||||
}
|
||||
})
|
||||
t.assert.strictEqual(str, ` ${colorizer.colors.green('foo')}: "bar"\n`)
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
const file10 = require("./file10.js")
|
||||
|
||||
module.exports = function () {
|
||||
file10()
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# json-buffer
|
||||
|
||||
JSON functions that can convert buffers!
|
||||
|
||||
[](http://travis-ci.org/dominictarr/json-buffer)
|
||||
|
||||
[](https://ci.testling.com/dominictarr/json-buffer)
|
||||
|
||||
JSON mangles buffers by converting to an array...
|
||||
which isn't helpful. json-buffers converts to base64 instead,
|
||||
and deconverts base64 to a buffer.
|
||||
|
||||
``` js
|
||||
var JSONB = require('json-buffer')
|
||||
var Buffer = require('buffer').Buffer
|
||||
|
||||
var str = JSONB.stringify(Buffer.from('hello there!'))
|
||||
|
||||
console.log(JSONB.parse(str)) //GET a BUFFER back
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,23 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2025-PRESENT Kevin Deng (https://github.com/sxzz)
|
||||
Copyright (c) 2014-2017 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2018-2021 Josh Junon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,192 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const tsutils = __importStar(require("ts-api-utils"));
|
||||
const ts = __importStar(require("typescript"));
|
||||
const util_1 = require("../util");
|
||||
const shared_1 = require("./enum-utils/shared");
|
||||
/**
|
||||
* @returns Whether the right type is an unsafe comparison against any left type.
|
||||
*/
|
||||
function typeViolates(leftTypeParts, rightType) {
|
||||
const leftEnumValueTypes = new Set(leftTypeParts.map(getEnumValueType));
|
||||
return ((leftEnumValueTypes.has(ts.TypeFlags.Number) && isNumberLike(rightType)) ||
|
||||
(leftEnumValueTypes.has(ts.TypeFlags.String) && isStringLike(rightType)));
|
||||
}
|
||||
function isNumberLike(type) {
|
||||
return tsutils
|
||||
.unionConstituents(type)
|
||||
.every(unionPart => tsutils
|
||||
.intersectionConstituents(unionPart)
|
||||
.some(intersectionPart => tsutils.isTypeFlagSet(intersectionPart, ts.TypeFlags.Number | ts.TypeFlags.NumberLike)));
|
||||
}
|
||||
function isStringLike(type) {
|
||||
return tsutils
|
||||
.unionConstituents(type)
|
||||
.every(unionPart => tsutils
|
||||
.intersectionConstituents(unionPart)
|
||||
.some(intersectionPart => tsutils.isTypeFlagSet(intersectionPart, ts.TypeFlags.String | ts.TypeFlags.StringLike)));
|
||||
}
|
||||
/**
|
||||
* @returns What type a type's enum value is (number or string), if either.
|
||||
*/
|
||||
function getEnumValueType(type) {
|
||||
return tsutils.isTypeFlagSet(type, ts.TypeFlags.EnumLike)
|
||||
? tsutils.isTypeFlagSet(type, ts.TypeFlags.NumberLiteral)
|
||||
? ts.TypeFlags.Number
|
||||
: ts.TypeFlags.String
|
||||
: undefined;
|
||||
}
|
||||
exports.default = (0, util_1.createRule)({
|
||||
name: 'no-unsafe-enum-comparison',
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description: 'Disallow comparing an enum value with a non-enum value',
|
||||
recommended: 'recommended',
|
||||
requiresTypeChecking: true,
|
||||
},
|
||||
hasSuggestions: true,
|
||||
messages: {
|
||||
mismatchedCase: 'The case statement does not have a shared enum type with the switch predicate.',
|
||||
mismatchedCondition: 'The two values in this comparison do not have a shared enum type.',
|
||||
replaceValueWithEnum: 'Replace with an enum value comparison.',
|
||||
},
|
||||
schema: [],
|
||||
},
|
||||
defaultOptions: [],
|
||||
create(context) {
|
||||
const parserServices = (0, util_1.getParserServices)(context);
|
||||
const typeChecker = parserServices.program.getTypeChecker();
|
||||
function isMismatchedComparison(leftType, rightType) {
|
||||
// Allow comparisons that don't have anything to do with enums:
|
||||
//
|
||||
// ```ts
|
||||
// 1 === 2;
|
||||
// ```
|
||||
const leftEnumTypes = (0, shared_1.getEnumTypes)(typeChecker, leftType);
|
||||
const rightEnumTypes = new Set((0, shared_1.getEnumTypes)(typeChecker, rightType));
|
||||
if (leftEnumTypes.length === 0 && rightEnumTypes.size === 0) {
|
||||
return false;
|
||||
}
|
||||
// Allow comparisons that share an enum type:
|
||||
//
|
||||
// ```ts
|
||||
// Fruit.Apple === Fruit.Banana;
|
||||
// ```
|
||||
for (const leftEnumType of leftEnumTypes) {
|
||||
if (rightEnumTypes.has(leftEnumType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// We need to split the type into the union type parts in order to find
|
||||
// valid enum comparisons like:
|
||||
//
|
||||
// ```ts
|
||||
// declare const something: Fruit | Vegetable;
|
||||
// something === Fruit.Apple;
|
||||
// ```
|
||||
const leftTypeParts = tsutils.unionConstituents(leftType);
|
||||
const rightTypeParts = tsutils.unionConstituents(rightType);
|
||||
// If a type exists in both sides, we consider this comparison safe:
|
||||
//
|
||||
// ```ts
|
||||
// declare const fruit: Fruit.Apple | 0;
|
||||
// fruit === 0;
|
||||
// ```
|
||||
for (const leftTypePart of leftTypeParts) {
|
||||
if (rightTypeParts.includes(leftTypePart)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (typeViolates(leftTypeParts, rightType) ||
|
||||
typeViolates(rightTypeParts, leftType));
|
||||
}
|
||||
return {
|
||||
'BinaryExpression[operator=/^[<>!=]?={0,2}$/]'(node) {
|
||||
const leftType = parserServices.getTypeAtLocation(node.left);
|
||||
const rightType = parserServices.getTypeAtLocation(node.right);
|
||||
if (isMismatchedComparison(leftType, rightType)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'mismatchedCondition',
|
||||
suggest: [
|
||||
{
|
||||
messageId: 'replaceValueWithEnum',
|
||||
fix(fixer) {
|
||||
// Replace the right side with an enum key if possible:
|
||||
//
|
||||
// ```ts
|
||||
// Fruit.Apple === 'apple'; // Fruit.Apple === Fruit.Apple
|
||||
// ```
|
||||
const leftEnumKey = (0, shared_1.getEnumKeyForLiteral)((0, shared_1.getEnumLiterals)(leftType), (0, util_1.getStaticValue)(node.right)?.value);
|
||||
if (leftEnumKey) {
|
||||
return fixer.replaceText(node.right, leftEnumKey);
|
||||
}
|
||||
// Replace the left side with an enum key if possible:
|
||||
//
|
||||
// ```ts
|
||||
// declare const fruit: Fruit;
|
||||
// 'apple' === Fruit.Apple; // Fruit.Apple === Fruit.Apple
|
||||
// ```
|
||||
const rightEnumKey = (0, shared_1.getEnumKeyForLiteral)((0, shared_1.getEnumLiterals)(rightType), (0, util_1.getStaticValue)(node.left)?.value);
|
||||
if (rightEnumKey) {
|
||||
return fixer.replaceText(node.left, rightEnumKey);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
},
|
||||
SwitchCase(node) {
|
||||
// Ignore `default` cases.
|
||||
if (node.test == null) {
|
||||
return;
|
||||
}
|
||||
const { parent } = node;
|
||||
const leftType = parserServices.getTypeAtLocation(parent.discriminant);
|
||||
const rightType = parserServices.getTypeAtLocation(node.test);
|
||||
if (isMismatchedComparison(leftType, rightType)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'mismatchedCase',
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,115 @@
|
||||
export {};
|
||||
|
||||
import * as webstreams from "stream/web";
|
||||
|
||||
type _ByteLengthQueuingStrategy = typeof globalThis extends { onmessage: any } ? {}
|
||||
: webstreams.ByteLengthQueuingStrategy;
|
||||
type _CompressionStream = typeof globalThis extends { onmessage: any } ? {} : webstreams.CompressionStream;
|
||||
type _CountQueuingStrategy = typeof globalThis extends { onmessage: any } ? {} : webstreams.CountQueuingStrategy;
|
||||
type _DecompressionStream = typeof globalThis extends { onmessage: any } ? {} : webstreams.DecompressionStream;
|
||||
type _QueuingStrategy<T = any> = typeof globalThis extends { onmessage: any } ? {} : webstreams.QueuingStrategy<T>;
|
||||
type _ReadableByteStreamController = typeof globalThis extends { onmessage: any } ? {}
|
||||
: webstreams.ReadableByteStreamController;
|
||||
type _ReadableStream<R = any> = typeof globalThis extends { onmessage: any } ? {} : webstreams.ReadableStream<R>;
|
||||
type _ReadableStreamBYOBReader = typeof globalThis extends { onmessage: any } ? {}
|
||||
: webstreams.ReadableStreamBYOBReader;
|
||||
type _ReadableStreamBYOBRequest = typeof globalThis extends { onmessage: any } ? {}
|
||||
: webstreams.ReadableStreamBYOBRequest;
|
||||
type _ReadableStreamDefaultController<R = any> = typeof globalThis extends { onmessage: any } ? {}
|
||||
: webstreams.ReadableStreamDefaultController<R>;
|
||||
type _ReadableStreamDefaultReader<R = any> = typeof globalThis extends { onmessage: any } ? {}
|
||||
: webstreams.ReadableStreamDefaultReader<R>;
|
||||
type _TextDecoderStream = typeof globalThis extends { onmessage: any } ? {} : webstreams.TextDecoderStream;
|
||||
type _TextEncoderStream = typeof globalThis extends { onmessage: any } ? {} : webstreams.TextEncoderStream;
|
||||
type _TransformStream<I = any, O = any> = typeof globalThis extends { onmessage: any } ? {}
|
||||
: webstreams.TransformStream<I, O>;
|
||||
type _TransformStreamDefaultController<O = any> = typeof globalThis extends { onmessage: any } ? {}
|
||||
: webstreams.TransformStreamDefaultController<O>;
|
||||
type _WritableStream<W = any> = typeof globalThis extends { onmessage: any } ? {} : webstreams.WritableStream<W>;
|
||||
type _WritableStreamDefaultController = typeof globalThis extends { onmessage: any } ? {}
|
||||
: webstreams.WritableStreamDefaultController;
|
||||
type _WritableStreamDefaultWriter<W = any> = typeof globalThis extends { onmessage: any } ? {}
|
||||
: webstreams.WritableStreamDefaultWriter<W>;
|
||||
|
||||
declare global {
|
||||
interface ByteLengthQueuingStrategy extends _ByteLengthQueuingStrategy {}
|
||||
var ByteLengthQueuingStrategy: typeof globalThis extends { onmessage: any; ByteLengthQueuingStrategy: infer T } ? T
|
||||
: typeof webstreams.ByteLengthQueuingStrategy;
|
||||
|
||||
interface CompressionStream extends _CompressionStream {}
|
||||
var CompressionStream: typeof globalThis extends {
|
||||
onmessage: any;
|
||||
CompressionStream: infer T;
|
||||
} ? T
|
||||
: typeof webstreams.CompressionStream;
|
||||
|
||||
interface CountQueuingStrategy extends _CountQueuingStrategy {}
|
||||
var CountQueuingStrategy: typeof globalThis extends { onmessage: any; CountQueuingStrategy: infer T } ? T
|
||||
: typeof webstreams.CountQueuingStrategy;
|
||||
|
||||
interface DecompressionStream extends _DecompressionStream {}
|
||||
var DecompressionStream: typeof globalThis extends {
|
||||
onmessage: any;
|
||||
DecompressionStream: infer T;
|
||||
} ? T
|
||||
: typeof webstreams.DecompressionStream;
|
||||
|
||||
interface QueuingStrategy<T = any> extends _QueuingStrategy<T> {}
|
||||
|
||||
interface ReadableByteStreamController extends _ReadableByteStreamController {}
|
||||
var ReadableByteStreamController: typeof globalThis extends
|
||||
{ onmessage: any; ReadableByteStreamController: infer T } ? T : typeof webstreams.ReadableByteStreamController;
|
||||
|
||||
interface ReadableStream<R = any> extends _ReadableStream<R> {}
|
||||
var ReadableStream: typeof globalThis extends { onmessage: any; ReadableStream: infer T } ? T
|
||||
: typeof webstreams.ReadableStream;
|
||||
|
||||
interface ReadableStreamBYOBReader extends _ReadableStreamBYOBReader {}
|
||||
var ReadableStreamBYOBReader: typeof globalThis extends { onmessage: any; ReadableStreamBYOBReader: infer T } ? T
|
||||
: typeof webstreams.ReadableStreamBYOBReader;
|
||||
|
||||
interface ReadableStreamBYOBRequest extends _ReadableStreamBYOBRequest {}
|
||||
var ReadableStreamBYOBRequest: typeof globalThis extends { onmessage: any; ReadableStreamBYOBRequest: infer T } ? T
|
||||
: typeof webstreams.ReadableStreamBYOBRequest;
|
||||
|
||||
interface ReadableStreamDefaultController<R = any> extends _ReadableStreamDefaultController<R> {}
|
||||
var ReadableStreamDefaultController: typeof globalThis extends
|
||||
{ onmessage: any; ReadableStreamDefaultController: infer T } ? T
|
||||
: typeof webstreams.ReadableStreamDefaultController;
|
||||
|
||||
interface ReadableStreamDefaultReader<R = any> extends _ReadableStreamDefaultReader<R> {}
|
||||
var ReadableStreamDefaultReader: typeof globalThis extends { onmessage: any; ReadableStreamDefaultReader: infer T }
|
||||
? T
|
||||
: typeof webstreams.ReadableStreamDefaultReader;
|
||||
|
||||
interface TextDecoderStream extends _TextDecoderStream {}
|
||||
var TextDecoderStream: typeof globalThis extends { onmessage: any; TextDecoderStream: infer T } ? T
|
||||
: typeof webstreams.TextDecoderStream;
|
||||
|
||||
interface TextEncoderStream extends _TextEncoderStream {}
|
||||
var TextEncoderStream: typeof globalThis extends { onmessage: any; TextEncoderStream: infer T } ? T
|
||||
: typeof webstreams.TextEncoderStream;
|
||||
|
||||
interface TransformStream<I = any, O = any> extends _TransformStream<I, O> {}
|
||||
var TransformStream: typeof globalThis extends { onmessage: any; TransformStream: infer T } ? T
|
||||
: typeof webstreams.TransformStream;
|
||||
|
||||
interface TransformStreamDefaultController<O = any> extends _TransformStreamDefaultController<O> {}
|
||||
var TransformStreamDefaultController: typeof globalThis extends
|
||||
{ onmessage: any; TransformStreamDefaultController: infer T } ? T
|
||||
: typeof webstreams.TransformStreamDefaultController;
|
||||
|
||||
interface WritableStream<W = any> extends _WritableStream<W> {}
|
||||
var WritableStream: typeof globalThis extends { onmessage: any; WritableStream: infer T } ? T
|
||||
: typeof webstreams.WritableStream;
|
||||
|
||||
interface WritableStreamDefaultController extends _WritableStreamDefaultController {}
|
||||
var WritableStreamDefaultController: typeof globalThis extends
|
||||
{ onmessage: any; WritableStreamDefaultController: infer T } ? T
|
||||
: typeof webstreams.WritableStreamDefaultController;
|
||||
|
||||
interface WritableStreamDefaultWriter<W = any> extends _WritableStreamDefaultWriter<W> {}
|
||||
var WritableStreamDefaultWriter: typeof globalThis extends { onmessage: any; WritableStreamDefaultWriter: infer T }
|
||||
? T
|
||||
: typeof webstreams.WritableStreamDefaultWriter;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Madeline Gurriarán
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isNodeEqual = isNodeEqual;
|
||||
const utils_1 = require("@typescript-eslint/utils");
|
||||
function isNodeEqual(a, b) {
|
||||
if (a.type !== b.type) {
|
||||
return false;
|
||||
}
|
||||
if (a.type === utils_1.AST_NODE_TYPES.ThisExpression &&
|
||||
b.type === utils_1.AST_NODE_TYPES.ThisExpression) {
|
||||
return true;
|
||||
}
|
||||
if (a.type === utils_1.AST_NODE_TYPES.Literal && b.type === utils_1.AST_NODE_TYPES.Literal) {
|
||||
return a.value === b.value;
|
||||
}
|
||||
if (a.type === utils_1.AST_NODE_TYPES.Identifier &&
|
||||
b.type === utils_1.AST_NODE_TYPES.Identifier) {
|
||||
return a.name === b.name;
|
||||
}
|
||||
if (a.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
||||
b.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
||||
return (isNodeEqual(a.property, b.property) && isNodeEqual(a.object, b.object));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { FixedSizeCodec, FixedSizeDecoder, FixedSizeEncoder } from '@solana/codecs-core';
|
||||
/**
|
||||
* Returns an encoder for 8-bit unsigned integers (`u8`).
|
||||
*
|
||||
* This encoder serializes `u8` values using a single byte.
|
||||
*
|
||||
* For more details, see {@link getU8Codec}.
|
||||
*
|
||||
* @returns A `FixedSizeEncoder<number | bigint, 1>` for encoding `u8` values.
|
||||
*
|
||||
* @example
|
||||
* Encoding a `u8` value.
|
||||
* ```ts
|
||||
* const encoder = getU8Encoder();
|
||||
* const bytes = encoder.encode(42); // 0x2a
|
||||
* ```
|
||||
*
|
||||
* @see {@link getU8Codec}
|
||||
*/
|
||||
export declare const getU8Encoder: () => FixedSizeEncoder<bigint | number, 1>;
|
||||
/**
|
||||
* Returns a decoder for 8-bit unsigned integers (`u8`).
|
||||
*
|
||||
* This decoder deserializes `u8` values from a single byte.
|
||||
*
|
||||
* For more details, see {@link getU8Codec}.
|
||||
*
|
||||
* @returns A `FixedSizeDecoder<number, 1>` for decoding `u8` values.
|
||||
*
|
||||
* @example
|
||||
* Decoding a `u8` value.
|
||||
* ```ts
|
||||
* const decoder = getU8Decoder();
|
||||
* const value = decoder.decode(new Uint8Array([0xff])); // 255
|
||||
* ```
|
||||
*
|
||||
* @see {@link getU8Codec}
|
||||
*/
|
||||
export declare const getU8Decoder: () => FixedSizeDecoder<number, 1>;
|
||||
/**
|
||||
* Returns a codec for encoding and decoding 8-bit unsigned integers (`u8`).
|
||||
*
|
||||
* This codec serializes `u8` values using a single byte.
|
||||
*
|
||||
* @returns A `FixedSizeCodec<number | bigint, number, 1>` for encoding and decoding `u8` values.
|
||||
*
|
||||
* @example
|
||||
* Encoding and decoding a `u8` value.
|
||||
* ```ts
|
||||
* const codec = getU8Codec();
|
||||
* const bytes = codec.encode(255); // 0xff
|
||||
* const value = codec.decode(bytes); // 255
|
||||
* ```
|
||||
*
|
||||
* @remarks
|
||||
* This codec supports values between `0` and `2^8 - 1` (0 to 255).
|
||||
* If you need larger integers, consider using {@link getU16Codec}, {@link getU32Codec}, or {@link getU64Codec}.
|
||||
* For signed integers, use {@link getI8Codec}.
|
||||
*
|
||||
* Separate {@link getU8Encoder} and {@link getU8Decoder} functions are available.
|
||||
*
|
||||
* ```ts
|
||||
* const bytes = getU8Encoder().encode(42);
|
||||
* const value = getU8Decoder().decode(bytes);
|
||||
* ```
|
||||
*
|
||||
* @see {@link getU8Encoder}
|
||||
* @see {@link getU8Decoder}
|
||||
*/
|
||||
export declare const getU8Codec: () => FixedSizeCodec<bigint | number, number, 1>;
|
||||
//# sourceMappingURL=u8.d.ts.map
|
||||
@@ -0,0 +1,166 @@
|
||||
import { a as namespaces, i as enabled, n as disable, o as humanize, r as enable$1, s as selectColor, t as createDebug$1 } from "./core.js";
|
||||
//#region src/browser.ts
|
||||
const colors = [
|
||||
"#0000CC",
|
||||
"#0000FF",
|
||||
"#0033CC",
|
||||
"#0033FF",
|
||||
"#0066CC",
|
||||
"#0066FF",
|
||||
"#0099CC",
|
||||
"#0099FF",
|
||||
"#00CC00",
|
||||
"#00CC33",
|
||||
"#00CC66",
|
||||
"#00CC99",
|
||||
"#00CCCC",
|
||||
"#00CCFF",
|
||||
"#3300CC",
|
||||
"#3300FF",
|
||||
"#3333CC",
|
||||
"#3333FF",
|
||||
"#3366CC",
|
||||
"#3366FF",
|
||||
"#3399CC",
|
||||
"#3399FF",
|
||||
"#33CC00",
|
||||
"#33CC33",
|
||||
"#33CC66",
|
||||
"#33CC99",
|
||||
"#33CCCC",
|
||||
"#33CCFF",
|
||||
"#6600CC",
|
||||
"#6600FF",
|
||||
"#6633CC",
|
||||
"#6633FF",
|
||||
"#66CC00",
|
||||
"#66CC33",
|
||||
"#9900CC",
|
||||
"#9900FF",
|
||||
"#9933CC",
|
||||
"#9933FF",
|
||||
"#99CC00",
|
||||
"#99CC33",
|
||||
"#CC0000",
|
||||
"#CC0033",
|
||||
"#CC0066",
|
||||
"#CC0099",
|
||||
"#CC00CC",
|
||||
"#CC00FF",
|
||||
"#CC3300",
|
||||
"#CC3333",
|
||||
"#CC3366",
|
||||
"#CC3399",
|
||||
"#CC33CC",
|
||||
"#CC33FF",
|
||||
"#CC6600",
|
||||
"#CC6633",
|
||||
"#CC9900",
|
||||
"#CC9933",
|
||||
"#CCCC00",
|
||||
"#CCCC33",
|
||||
"#FF0000",
|
||||
"#FF0033",
|
||||
"#FF0066",
|
||||
"#FF0099",
|
||||
"#FF00CC",
|
||||
"#FF00FF",
|
||||
"#FF3300",
|
||||
"#FF3333",
|
||||
"#FF3366",
|
||||
"#FF3399",
|
||||
"#FF33CC",
|
||||
"#FF33FF",
|
||||
"#FF6600",
|
||||
"#FF6633",
|
||||
"#FF9900",
|
||||
"#FF9933",
|
||||
"#FFCC00",
|
||||
"#FFCC33"
|
||||
];
|
||||
/**
|
||||
* Colorize log arguments if enabled.
|
||||
*/
|
||||
function formatArgs(diff, args) {
|
||||
const { useColors } = this;
|
||||
args[0] = `${(useColors ? "%c" : "") + this.namespace + (useColors ? " %c" : " ") + args[0] + (useColors ? "%c " : " ")}+${this.humanize(diff)}`;
|
||||
if (!useColors) return;
|
||||
const c = `color: ${this.color}`;
|
||||
args.splice(1, 0, c, "color: inherit");
|
||||
let index = 0;
|
||||
let lastC = 0;
|
||||
args[0].replace(/%[a-z%]/gi, (match) => {
|
||||
if (match === "%%") return;
|
||||
index++;
|
||||
if (match === "%c") lastC = index;
|
||||
});
|
||||
args.splice(lastC, 0, c);
|
||||
}
|
||||
/**
|
||||
* Invokes `console.debug()` when available.
|
||||
* No-op when `console.debug` is not a "function".
|
||||
* If `console.debug` is not available, falls back
|
||||
* to `console.log`.
|
||||
*/
|
||||
const log = console.debug || console.log || (() => {});
|
||||
const storage = localstorage();
|
||||
const defaultOptions = {
|
||||
useColors: true,
|
||||
formatArgs,
|
||||
formatters: {
|
||||
/**
|
||||
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
|
||||
*/
|
||||
j(v) {
|
||||
try {
|
||||
return JSON.stringify(v);
|
||||
} catch (error) {
|
||||
return `[UnexpectedJSONParseError]: ${error.message}`;
|
||||
}
|
||||
} },
|
||||
inspectOpts: {},
|
||||
humanize,
|
||||
log
|
||||
};
|
||||
function createDebug(namespace, options) {
|
||||
var _ref;
|
||||
const color = (_ref = options && options.color) !== null && _ref !== void 0 ? _ref : selectColor(colors, namespace);
|
||||
return createDebug$1(namespace, Object.assign(defaultOptions, { color }, options));
|
||||
}
|
||||
/**
|
||||
* Localstorage attempts to return the localstorage.
|
||||
*
|
||||
* This is necessary because safari throws
|
||||
* when a user disables cookies/localstorage
|
||||
* and you attempt to access it.
|
||||
*/
|
||||
function localstorage() {
|
||||
try {
|
||||
return localStorage;
|
||||
} catch (_unused) {}
|
||||
}
|
||||
function load() {
|
||||
let r;
|
||||
try {
|
||||
r = storage.getItem("debug") || storage.getItem("DEBUG");
|
||||
} catch (_unused2) {}
|
||||
if (!r && typeof process !== "undefined" && "env" in process) r = process.env.DEBUG;
|
||||
return r || "";
|
||||
}
|
||||
function save(namespaces) {
|
||||
try {
|
||||
if (namespaces) storage.setItem("debug", namespaces);
|
||||
else storage.removeItem("debug");
|
||||
} catch (_unused3) {}
|
||||
}
|
||||
/**
|
||||
* Enables a debug mode by namespaces. This can include modes
|
||||
* separated by a colon and wildcards.
|
||||
*/
|
||||
function enable(namespaces) {
|
||||
save(namespaces);
|
||||
enable$1(namespaces);
|
||||
}
|
||||
enable$1(load());
|
||||
//#endregion
|
||||
export { createDebug, disable, enable, enabled, namespaces };
|
||||
@@ -0,0 +1,239 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.OptionKeys = void 0;
|
||||
const utils_1 = require("@typescript-eslint/utils");
|
||||
const util_1 = require("../util");
|
||||
var OptionKeys;
|
||||
(function (OptionKeys) {
|
||||
OptionKeys["ArrayDestructuring"] = "arrayDestructuring";
|
||||
OptionKeys["ArrowParameter"] = "arrowParameter";
|
||||
OptionKeys["MemberVariableDeclaration"] = "memberVariableDeclaration";
|
||||
OptionKeys["ObjectDestructuring"] = "objectDestructuring";
|
||||
OptionKeys["Parameter"] = "parameter";
|
||||
OptionKeys["PropertyDeclaration"] = "propertyDeclaration";
|
||||
OptionKeys["VariableDeclaration"] = "variableDeclaration";
|
||||
OptionKeys["VariableDeclarationIgnoreFunction"] = "variableDeclarationIgnoreFunction";
|
||||
})(OptionKeys || (exports.OptionKeys = OptionKeys = {}));
|
||||
exports.default = (0, util_1.createRule)({
|
||||
name: 'typedef',
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
deprecated: {
|
||||
deprecatedSince: '8.33.0',
|
||||
message: 'This is an old rule that is no longer recommended for use.',
|
||||
},
|
||||
docs: {
|
||||
description: 'Require type annotations in certain places',
|
||||
},
|
||||
messages: {
|
||||
expectedTypedef: 'Expected a type annotation.',
|
||||
expectedTypedefNamed: 'Expected {{name}} to have a type annotation.',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
[OptionKeys.ArrayDestructuring]: {
|
||||
type: 'boolean',
|
||||
description: 'Whether to enforce type annotations on variables declared using array destructuring.',
|
||||
},
|
||||
[OptionKeys.ArrowParameter]: {
|
||||
type: 'boolean',
|
||||
description: 'Whether to enforce type annotations for parameters of arrow functions.',
|
||||
},
|
||||
[OptionKeys.MemberVariableDeclaration]: {
|
||||
type: 'boolean',
|
||||
description: 'Whether to enforce type annotations on member variables of classes.',
|
||||
},
|
||||
[OptionKeys.ObjectDestructuring]: {
|
||||
type: 'boolean',
|
||||
description: 'Whether to enforce type annotations on variables declared using object destructuring.',
|
||||
},
|
||||
[OptionKeys.Parameter]: {
|
||||
type: 'boolean',
|
||||
description: 'Whether to enforce type annotations for parameters of functions and methods.',
|
||||
},
|
||||
[OptionKeys.PropertyDeclaration]: {
|
||||
type: 'boolean',
|
||||
description: 'Whether to enforce type annotations for properties of interfaces and types.',
|
||||
},
|
||||
[OptionKeys.VariableDeclaration]: {
|
||||
type: 'boolean',
|
||||
description: 'Whether to enforce type annotations for variable declarations, excluding array and object destructuring.',
|
||||
},
|
||||
[OptionKeys.VariableDeclarationIgnoreFunction]: {
|
||||
type: 'boolean',
|
||||
description: 'Whether to ignore variable declarations for non-arrow and arrow functions.',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
defaultOptions: [
|
||||
{
|
||||
[OptionKeys.ArrayDestructuring]: false,
|
||||
[OptionKeys.ArrowParameter]: false,
|
||||
[OptionKeys.MemberVariableDeclaration]: false,
|
||||
[OptionKeys.ObjectDestructuring]: false,
|
||||
[OptionKeys.Parameter]: false,
|
||||
[OptionKeys.PropertyDeclaration]: false,
|
||||
[OptionKeys.VariableDeclaration]: false,
|
||||
[OptionKeys.VariableDeclarationIgnoreFunction]: false,
|
||||
},
|
||||
],
|
||||
create(context, [{ arrayDestructuring, arrowParameter, memberVariableDeclaration, objectDestructuring, parameter, propertyDeclaration, variableDeclaration, variableDeclarationIgnoreFunction, },]) {
|
||||
function report(location, name) {
|
||||
context.report({
|
||||
node: location,
|
||||
messageId: name ? 'expectedTypedefNamed' : 'expectedTypedef',
|
||||
data: { name },
|
||||
});
|
||||
}
|
||||
function getNodeName(node) {
|
||||
return node.type === utils_1.AST_NODE_TYPES.Identifier ? node.name : undefined;
|
||||
}
|
||||
function isForOfStatementContext(node) {
|
||||
let current = node.parent;
|
||||
while (current) {
|
||||
switch (current.type) {
|
||||
case utils_1.AST_NODE_TYPES.VariableDeclarator:
|
||||
case utils_1.AST_NODE_TYPES.VariableDeclaration:
|
||||
case utils_1.AST_NODE_TYPES.ObjectPattern:
|
||||
case utils_1.AST_NODE_TYPES.ArrayPattern:
|
||||
case utils_1.AST_NODE_TYPES.Property:
|
||||
current = current.parent;
|
||||
break;
|
||||
case utils_1.AST_NODE_TYPES.ForOfStatement:
|
||||
return true;
|
||||
default:
|
||||
current = undefined;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function checkParameters(params) {
|
||||
for (const param of params) {
|
||||
let annotationNode;
|
||||
switch (param.type) {
|
||||
case utils_1.AST_NODE_TYPES.AssignmentPattern:
|
||||
annotationNode = param.left;
|
||||
break;
|
||||
case utils_1.AST_NODE_TYPES.TSParameterProperty:
|
||||
annotationNode = param.parameter;
|
||||
// Check TS parameter property with default value like `constructor(private param: string = 'something') {}`
|
||||
if (annotationNode.type === utils_1.AST_NODE_TYPES.AssignmentPattern) {
|
||||
annotationNode = annotationNode.left;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
annotationNode = param;
|
||||
break;
|
||||
}
|
||||
if (!annotationNode.typeAnnotation) {
|
||||
report(param, getNodeName(param));
|
||||
}
|
||||
}
|
||||
}
|
||||
function isVariableDeclarationIgnoreFunction(node) {
|
||||
return (variableDeclarationIgnoreFunction === true &&
|
||||
(node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
||||
node.type === utils_1.AST_NODE_TYPES.FunctionExpression));
|
||||
}
|
||||
function isAncestorHasTypeAnnotation(node) {
|
||||
let ancestor = node.parent;
|
||||
while (ancestor) {
|
||||
if ((ancestor.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
|
||||
ancestor.type === utils_1.AST_NODE_TYPES.ArrayPattern) &&
|
||||
ancestor.typeAnnotation) {
|
||||
return true;
|
||||
}
|
||||
ancestor = ancestor.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
...(arrayDestructuring && {
|
||||
ArrayPattern(node) {
|
||||
if (node.parent.type === utils_1.AST_NODE_TYPES.RestElement &&
|
||||
node.parent.typeAnnotation) {
|
||||
return;
|
||||
}
|
||||
if (!node.typeAnnotation &&
|
||||
!isForOfStatementContext(node) &&
|
||||
!isAncestorHasTypeAnnotation(node) &&
|
||||
node.parent.type !== utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
||||
report(node);
|
||||
}
|
||||
},
|
||||
}),
|
||||
...(arrowParameter && {
|
||||
ArrowFunctionExpression(node) {
|
||||
checkParameters(node.params);
|
||||
},
|
||||
}),
|
||||
...(memberVariableDeclaration && {
|
||||
PropertyDefinition(node) {
|
||||
if (!(node.value && isVariableDeclarationIgnoreFunction(node.value)) &&
|
||||
!node.typeAnnotation) {
|
||||
report(node, node.key.type === utils_1.AST_NODE_TYPES.Identifier
|
||||
? node.key.name
|
||||
: undefined);
|
||||
}
|
||||
},
|
||||
}),
|
||||
...(parameter && {
|
||||
'FunctionDeclaration, FunctionExpression'(node) {
|
||||
checkParameters(node.params);
|
||||
},
|
||||
}),
|
||||
...(objectDestructuring && {
|
||||
ObjectPattern(node) {
|
||||
if (!node.typeAnnotation &&
|
||||
!isForOfStatementContext(node) &&
|
||||
!isAncestorHasTypeAnnotation(node)) {
|
||||
report(node);
|
||||
}
|
||||
},
|
||||
}),
|
||||
...(propertyDeclaration && {
|
||||
'TSIndexSignature, TSPropertySignature'(node) {
|
||||
if (!node.typeAnnotation) {
|
||||
report(node, node.type === utils_1.AST_NODE_TYPES.TSPropertySignature
|
||||
? getNodeName(node.key)
|
||||
: undefined);
|
||||
}
|
||||
},
|
||||
}),
|
||||
VariableDeclarator(node) {
|
||||
if (!variableDeclaration ||
|
||||
node.id.typeAnnotation ||
|
||||
(node.id.type === utils_1.AST_NODE_TYPES.ArrayPattern &&
|
||||
!arrayDestructuring) ||
|
||||
(node.id.type === utils_1.AST_NODE_TYPES.ObjectPattern &&
|
||||
!objectDestructuring) ||
|
||||
(node.init && isVariableDeclarationIgnoreFunction(node.init))) {
|
||||
return;
|
||||
}
|
||||
let current = node.parent;
|
||||
while (current) {
|
||||
switch (current.type) {
|
||||
case utils_1.AST_NODE_TYPES.VariableDeclaration:
|
||||
// Keep looking upwards
|
||||
current = current.parent;
|
||||
break;
|
||||
case utils_1.AST_NODE_TYPES.ForOfStatement:
|
||||
case utils_1.AST_NODE_TYPES.ForInStatement:
|
||||
// Stop traversing and don't report an error
|
||||
return;
|
||||
default:
|
||||
// Stop traversing
|
||||
current = undefined;
|
||||
break;
|
||||
}
|
||||
}
|
||||
report(node, getNodeName(node.id));
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
var test = require('tape');
|
||||
var parse = require('../');
|
||||
|
||||
test('boolean default true', function (t) {
|
||||
var argv = parse([], {
|
||||
boolean: 'sometrue',
|
||||
default: { sometrue: true },
|
||||
});
|
||||
t.equal(argv.sometrue, true);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('boolean default false', function (t) {
|
||||
var argv = parse([], {
|
||||
boolean: 'somefalse',
|
||||
default: { somefalse: false },
|
||||
});
|
||||
t.equal(argv.somefalse, false);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('boolean default to null', function (t) {
|
||||
var argv = parse([], {
|
||||
boolean: 'maybe',
|
||||
default: { maybe: null },
|
||||
});
|
||||
t.equal(argv.maybe, null);
|
||||
|
||||
var argvLong = parse(['--maybe'], {
|
||||
boolean: 'maybe',
|
||||
default: { maybe: null },
|
||||
});
|
||||
t.equal(argvLong.maybe, true);
|
||||
t.end();
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
function e(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var t,n={exports:{}};var r=(t||(t=1,function(e){var t=Object.prototype.hasOwnProperty,n="~";function r(){}function o(e,t,n){this.fn=e,this.context=t,this.once=n||!1}function s(e,t,r,s,i){if("function"!=typeof r)throw new TypeError("The listener must be a function");var c=new o(r,s||e,i),f=n?n+t:t;return e._events[f]?e._events[f].fn?e._events[f]=[e._events[f],c]:e._events[f].push(c):(e._events[f]=c,e._eventsCount++),e}function i(e,t){0===--e._eventsCount?e._events=new r:delete e._events[t]}function c(){this._events=new r,this._eventsCount=0}Object.create&&(r.prototype=Object.create(null),(new r).__proto__||(n=!1)),c.prototype.eventNames=function(){var e,r,o=[];if(0===this._eventsCount)return o;for(r in e=this._events)t.call(e,r)&&o.push(n?r.slice(1):r);return Object.getOwnPropertySymbols?o.concat(Object.getOwnPropertySymbols(e)):o},c.prototype.listeners=function(e){var t=n?n+e:e,r=this._events[t];if(!r)return[];if(r.fn)return[r.fn];for(var o=0,s=r.length,i=new Array(s);o<s;o++)i[o]=r[o].fn;return i},c.prototype.listenerCount=function(e){var t=n?n+e:e,r=this._events[t];return r?r.fn?1:r.length:0},c.prototype.emit=function(e,t,r,o,s,i){var c=n?n+e:e;if(!this._events[c])return!1;var f,a,u=this._events[c],l=arguments.length;if(u.fn){switch(u.once&&this.removeListener(e,u.fn,void 0,!0),l){case 1:return u.fn.call(u.context),!0;case 2:return u.fn.call(u.context,t),!0;case 3:return u.fn.call(u.context,t,r),!0;case 4:return u.fn.call(u.context,t,r,o),!0;case 5:return u.fn.call(u.context,t,r,o,s),!0;case 6:return u.fn.call(u.context,t,r,o,s,i),!0}for(a=1,f=new Array(l-1);a<l;a++)f[a-1]=arguments[a];u.fn.apply(u.context,f)}else{var p,v=u.length;for(a=0;a<v;a++)switch(u[a].once&&this.removeListener(e,u[a].fn,void 0,!0),l){case 1:u[a].fn.call(u[a].context);break;case 2:u[a].fn.call(u[a].context,t);break;case 3:u[a].fn.call(u[a].context,t,r);break;case 4:u[a].fn.call(u[a].context,t,r,o);break;default:if(!f)for(p=1,f=new Array(l-1);p<l;p++)f[p-1]=arguments[p];u[a].fn.apply(u[a].context,f)}}return!0},c.prototype.on=function(e,t,n){return s(this,e,t,n,!1)},c.prototype.once=function(e,t,n){return s(this,e,t,n,!0)},c.prototype.removeListener=function(e,t,r,o){var s=n?n+e:e;if(!this._events[s])return this;if(!t)return i(this,s),this;var c=this._events[s];if(c.fn)c.fn!==t||o&&!c.once||r&&c.context!==r||i(this,s);else{for(var f=0,a=[],u=c.length;f<u;f++)(c[f].fn!==t||o&&!c[f].once||r&&c[f].context!==r)&&a.push(c[f]);a.length?this._events[s]=1===a.length?a[0]:a:i(this,s)}return this},c.prototype.removeAllListeners=function(e){var t;return e?(t=n?n+e:e,this._events[t]&&i(this,t)):(this._events=new r,this._eventsCount=0),this},c.prototype.off=c.prototype.removeListener,c.prototype.addListener=c.prototype.on,c.prefixed=n,c.EventEmitter=c,e.exports=c}(n)),n.exports),o=e(r);export{o as EventEmitter,o as default};//# sourceMappingURL=eventemitter3.esm.min.js.map
|
||||
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
* SHA3 (keccak) hash function, based on a new "Sponge function" design.
|
||||
* Different from older hashes, the internal state is bigger than output size.
|
||||
*
|
||||
* Check out [FIPS-202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf),
|
||||
* [Website](https://keccak.team/keccak.html),
|
||||
* [the differences between SHA-3 and Keccak](https://crypto.stackexchange.com/questions/15727/what-are-the-key-differences-between-the-draft-sha-3-standard-and-the-keccak-sub).
|
||||
*
|
||||
* Check out `sha3-addons` module for cSHAKE, k12, and others.
|
||||
* @module
|
||||
*/
|
||||
import { rotlBH, rotlBL, rotlSH, rotlSL, split } from "./_u64.js";
|
||||
// prettier-ignore
|
||||
import { abytes, aexists, anumber, aoutput, clean, createHasher, createXOFer, Hash, swap32IfBE, toBytes, u32 } from "./utils.js";
|
||||
// No __PURE__ annotations in sha3 header:
|
||||
// EVERYTHING is in fact used on every export.
|
||||
// Various per round constants calculations
|
||||
const _0n = BigInt(0);
|
||||
const _1n = BigInt(1);
|
||||
const _2n = BigInt(2);
|
||||
const _7n = BigInt(7);
|
||||
const _256n = BigInt(256);
|
||||
const _0x71n = BigInt(0x71);
|
||||
const SHA3_PI = [];
|
||||
const SHA3_ROTL = [];
|
||||
const _SHA3_IOTA = [];
|
||||
for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
|
||||
// Pi
|
||||
[x, y] = [y, (2 * x + 3 * y) % 5];
|
||||
SHA3_PI.push(2 * (5 * y + x));
|
||||
// Rotational
|
||||
SHA3_ROTL.push((((round + 1) * (round + 2)) / 2) % 64);
|
||||
// Iota
|
||||
let t = _0n;
|
||||
for (let j = 0; j < 7; j++) {
|
||||
R = ((R << _1n) ^ ((R >> _7n) * _0x71n)) % _256n;
|
||||
if (R & _2n)
|
||||
t ^= _1n << ((_1n << /* @__PURE__ */ BigInt(j)) - _1n);
|
||||
}
|
||||
_SHA3_IOTA.push(t);
|
||||
}
|
||||
const IOTAS = split(_SHA3_IOTA, true);
|
||||
const SHA3_IOTA_H = IOTAS[0];
|
||||
const SHA3_IOTA_L = IOTAS[1];
|
||||
// Left rotation (without 0, 32, 64)
|
||||
const rotlH = (h, l, s) => (s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s));
|
||||
const rotlL = (h, l, s) => (s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s));
|
||||
/** `keccakf1600` internal function, additionally allows to adjust round count. */
|
||||
export function keccakP(s, rounds = 24) {
|
||||
const B = new Uint32Array(5 * 2);
|
||||
// NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js)
|
||||
for (let round = 24 - rounds; round < 24; round++) {
|
||||
// Theta θ
|
||||
for (let x = 0; x < 10; x++)
|
||||
B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
|
||||
for (let x = 0; x < 10; x += 2) {
|
||||
const idx1 = (x + 8) % 10;
|
||||
const idx0 = (x + 2) % 10;
|
||||
const B0 = B[idx0];
|
||||
const B1 = B[idx0 + 1];
|
||||
const Th = rotlH(B0, B1, 1) ^ B[idx1];
|
||||
const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
|
||||
for (let y = 0; y < 50; y += 10) {
|
||||
s[x + y] ^= Th;
|
||||
s[x + y + 1] ^= Tl;
|
||||
}
|
||||
}
|
||||
// Rho (ρ) and Pi (π)
|
||||
let curH = s[2];
|
||||
let curL = s[3];
|
||||
for (let t = 0; t < 24; t++) {
|
||||
const shift = SHA3_ROTL[t];
|
||||
const Th = rotlH(curH, curL, shift);
|
||||
const Tl = rotlL(curH, curL, shift);
|
||||
const PI = SHA3_PI[t];
|
||||
curH = s[PI];
|
||||
curL = s[PI + 1];
|
||||
s[PI] = Th;
|
||||
s[PI + 1] = Tl;
|
||||
}
|
||||
// Chi (χ)
|
||||
for (let y = 0; y < 50; y += 10) {
|
||||
for (let x = 0; x < 10; x++)
|
||||
B[x] = s[y + x];
|
||||
for (let x = 0; x < 10; x++)
|
||||
s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
|
||||
}
|
||||
// Iota (ι)
|
||||
s[0] ^= SHA3_IOTA_H[round];
|
||||
s[1] ^= SHA3_IOTA_L[round];
|
||||
}
|
||||
clean(B);
|
||||
}
|
||||
/** Keccak sponge function. */
|
||||
export class Keccak extends Hash {
|
||||
// NOTE: we accept arguments in bytes instead of bits here.
|
||||
constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) {
|
||||
super();
|
||||
this.pos = 0;
|
||||
this.posOut = 0;
|
||||
this.finished = false;
|
||||
this.destroyed = false;
|
||||
this.enableXOF = false;
|
||||
this.blockLen = blockLen;
|
||||
this.suffix = suffix;
|
||||
this.outputLen = outputLen;
|
||||
this.enableXOF = enableXOF;
|
||||
this.rounds = rounds;
|
||||
// Can be passed from user as dkLen
|
||||
anumber(outputLen);
|
||||
// 1600 = 5x5 matrix of 64bit. 1600 bits === 200 bytes
|
||||
// 0 < blockLen < 200
|
||||
if (!(0 < blockLen && blockLen < 200))
|
||||
throw new Error('only keccak-f1600 function is supported');
|
||||
this.state = new Uint8Array(200);
|
||||
this.state32 = u32(this.state);
|
||||
}
|
||||
clone() {
|
||||
return this._cloneInto();
|
||||
}
|
||||
keccak() {
|
||||
swap32IfBE(this.state32);
|
||||
keccakP(this.state32, this.rounds);
|
||||
swap32IfBE(this.state32);
|
||||
this.posOut = 0;
|
||||
this.pos = 0;
|
||||
}
|
||||
update(data) {
|
||||
aexists(this);
|
||||
data = toBytes(data);
|
||||
abytes(data);
|
||||
const { blockLen, state } = this;
|
||||
const len = data.length;
|
||||
for (let pos = 0; pos < len;) {
|
||||
const take = Math.min(blockLen - this.pos, len - pos);
|
||||
for (let i = 0; i < take; i++)
|
||||
state[this.pos++] ^= data[pos++];
|
||||
if (this.pos === blockLen)
|
||||
this.keccak();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
finish() {
|
||||
if (this.finished)
|
||||
return;
|
||||
this.finished = true;
|
||||
const { state, suffix, pos, blockLen } = this;
|
||||
// Do the padding
|
||||
state[pos] ^= suffix;
|
||||
if ((suffix & 0x80) !== 0 && pos === blockLen - 1)
|
||||
this.keccak();
|
||||
state[blockLen - 1] ^= 0x80;
|
||||
this.keccak();
|
||||
}
|
||||
writeInto(out) {
|
||||
aexists(this, false);
|
||||
abytes(out);
|
||||
this.finish();
|
||||
const bufferOut = this.state;
|
||||
const { blockLen } = this;
|
||||
for (let pos = 0, len = out.length; pos < len;) {
|
||||
if (this.posOut >= blockLen)
|
||||
this.keccak();
|
||||
const take = Math.min(blockLen - this.posOut, len - pos);
|
||||
out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
|
||||
this.posOut += take;
|
||||
pos += take;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
xofInto(out) {
|
||||
// Sha3/Keccak usage with XOF is probably mistake, only SHAKE instances can do XOF
|
||||
if (!this.enableXOF)
|
||||
throw new Error('XOF is not possible for this instance');
|
||||
return this.writeInto(out);
|
||||
}
|
||||
xof(bytes) {
|
||||
anumber(bytes);
|
||||
return this.xofInto(new Uint8Array(bytes));
|
||||
}
|
||||
digestInto(out) {
|
||||
aoutput(out, this);
|
||||
if (this.finished)
|
||||
throw new Error('digest() was already called');
|
||||
this.writeInto(out);
|
||||
this.destroy();
|
||||
return out;
|
||||
}
|
||||
digest() {
|
||||
return this.digestInto(new Uint8Array(this.outputLen));
|
||||
}
|
||||
destroy() {
|
||||
this.destroyed = true;
|
||||
clean(this.state);
|
||||
}
|
||||
_cloneInto(to) {
|
||||
const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
|
||||
to || (to = new Keccak(blockLen, suffix, outputLen, enableXOF, rounds));
|
||||
to.state32.set(this.state32);
|
||||
to.pos = this.pos;
|
||||
to.posOut = this.posOut;
|
||||
to.finished = this.finished;
|
||||
to.rounds = rounds;
|
||||
// Suffix can change in cSHAKE
|
||||
to.suffix = suffix;
|
||||
to.outputLen = outputLen;
|
||||
to.enableXOF = enableXOF;
|
||||
to.destroyed = this.destroyed;
|
||||
return to;
|
||||
}
|
||||
}
|
||||
const gen = (suffix, blockLen, outputLen) => createHasher(() => new Keccak(blockLen, suffix, outputLen));
|
||||
/** SHA3-224 hash function. */
|
||||
export const sha3_224 = /* @__PURE__ */ (() => gen(0x06, 144, 224 / 8))();
|
||||
/** SHA3-256 hash function. Different from keccak-256. */
|
||||
export const sha3_256 = /* @__PURE__ */ (() => gen(0x06, 136, 256 / 8))();
|
||||
/** SHA3-384 hash function. */
|
||||
export const sha3_384 = /* @__PURE__ */ (() => gen(0x06, 104, 384 / 8))();
|
||||
/** SHA3-512 hash function. */
|
||||
export const sha3_512 = /* @__PURE__ */ (() => gen(0x06, 72, 512 / 8))();
|
||||
/** keccak-224 hash function. */
|
||||
export const keccak_224 = /* @__PURE__ */ (() => gen(0x01, 144, 224 / 8))();
|
||||
/** keccak-256 hash function. Different from SHA3-256. */
|
||||
export const keccak_256 = /* @__PURE__ */ (() => gen(0x01, 136, 256 / 8))();
|
||||
/** keccak-384 hash function. */
|
||||
export const keccak_384 = /* @__PURE__ */ (() => gen(0x01, 104, 384 / 8))();
|
||||
/** keccak-512 hash function. */
|
||||
export const keccak_512 = /* @__PURE__ */ (() => gen(0x01, 72, 512 / 8))();
|
||||
const genShake = (suffix, blockLen, outputLen) => createXOFer((opts = {}) => new Keccak(blockLen, suffix, opts.dkLen === undefined ? outputLen : opts.dkLen, true));
|
||||
/** SHAKE128 XOF with 128-bit security. */
|
||||
export const shake128 = /* @__PURE__ */ (() => genShake(0x1f, 168, 128 / 8))();
|
||||
/** SHAKE256 XOF with 256-bit security. */
|
||||
export const shake256 = /* @__PURE__ */ (() => genShake(0x1f, 136, 256 / 8))();
|
||||
//# sourceMappingURL=sha3.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
require('../lib/tsc.js')
|
||||
@@ -0,0 +1,548 @@
|
||||
# ws: a Node.js WebSocket library
|
||||
|
||||
[](https://www.npmjs.com/package/ws)
|
||||
[](https://github.com/websockets/ws/actions?query=workflow%3ACI+branch%3Amaster)
|
||||
[](https://coveralls.io/github/websockets/ws)
|
||||
|
||||
ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
|
||||
server implementation.
|
||||
|
||||
Passes the quite extensive Autobahn test suite: [server][server-report],
|
||||
[client][client-report].
|
||||
|
||||
**Note**: This module does not work in the browser. The client in the docs is a
|
||||
reference to a backend with the role of a client in the WebSocket communication.
|
||||
Browser clients must use the native
|
||||
[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
|
||||
object. To make the same code work seamlessly on Node.js and the browser, you
|
||||
can use one of the many wrappers available on npm, like
|
||||
[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Protocol support](#protocol-support)
|
||||
- [Installing](#installing)
|
||||
- [Opt-in for performance](#opt-in-for-performance)
|
||||
- [Legacy opt-in for performance](#legacy-opt-in-for-performance)
|
||||
- [API docs](#api-docs)
|
||||
- [WebSocket compression](#websocket-compression)
|
||||
- [Usage examples](#usage-examples)
|
||||
- [Sending and receiving text data](#sending-and-receiving-text-data)
|
||||
- [Sending binary data](#sending-binary-data)
|
||||
- [Simple server](#simple-server)
|
||||
- [External HTTP/S server](#external-https-server)
|
||||
- [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server)
|
||||
- [Client authentication](#client-authentication)
|
||||
- [Server broadcast](#server-broadcast)
|
||||
- [Round-trip time](#round-trip-time)
|
||||
- [Use the Node.js streams API](#use-the-nodejs-streams-api)
|
||||
- [Other examples](#other-examples)
|
||||
- [FAQ](#faq)
|
||||
- [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client)
|
||||
- [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections)
|
||||
- [How to connect via a proxy?](#how-to-connect-via-a-proxy)
|
||||
- [Changelog](#changelog)
|
||||
- [License](#license)
|
||||
|
||||
## Protocol support
|
||||
|
||||
- **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
|
||||
- **HyBi drafts 13-17** (Current default, alternatively option
|
||||
`protocolVersion: 13`)
|
||||
|
||||
## Installing
|
||||
|
||||
```
|
||||
npm install ws
|
||||
```
|
||||
|
||||
### Opt-in for performance
|
||||
|
||||
[bufferutil][] is an optional module that can be installed alongside the ws
|
||||
module:
|
||||
|
||||
```
|
||||
npm install --save-optional bufferutil
|
||||
```
|
||||
|
||||
This is a binary addon that improves the performance of certain operations such
|
||||
as masking and unmasking the data payload of the WebSocket frames. Prebuilt
|
||||
binaries are available for the most popular platforms, so you don't necessarily
|
||||
need to have a C++ compiler installed on your machine.
|
||||
|
||||
To force ws to not use bufferutil, use the
|
||||
[`WS_NO_BUFFER_UTIL`](./doc/ws.md#ws_no_buffer_util) environment variable. This
|
||||
can be useful to enhance security in systems where a user can put a package in
|
||||
the package search path of an application of another user, due to how the
|
||||
Node.js resolver algorithm works.
|
||||
|
||||
#### Legacy opt-in for performance
|
||||
|
||||
If you are running on an old version of Node.js (prior to v18.14.0), ws also
|
||||
supports the [utf-8-validate][] module:
|
||||
|
||||
```
|
||||
npm install --save-optional utf-8-validate
|
||||
```
|
||||
|
||||
This contains a binary polyfill for [`buffer.isUtf8()`][].
|
||||
|
||||
To force ws not to use utf-8-validate, use the
|
||||
[`WS_NO_UTF_8_VALIDATE`](./doc/ws.md#ws_no_utf_8_validate) environment variable.
|
||||
|
||||
## API docs
|
||||
|
||||
See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and
|
||||
utility functions.
|
||||
|
||||
## WebSocket compression
|
||||
|
||||
ws supports the [permessage-deflate extension][permessage-deflate] which enables
|
||||
the client and server to negotiate a compression algorithm and its parameters,
|
||||
and then selectively apply it to the data payloads of each WebSocket message.
|
||||
|
||||
The extension is disabled by default on the server and enabled by default on the
|
||||
client. It adds a significant overhead in terms of performance and memory
|
||||
consumption so we suggest to enable it only if it is really needed.
|
||||
|
||||
Note that Node.js has a variety of issues with high-performance compression,
|
||||
where increased concurrency, especially on Linux, can lead to [catastrophic
|
||||
memory fragmentation][node-zlib-bug] and slow performance. If you intend to use
|
||||
permessage-deflate in production, it is worthwhile to set up a test
|
||||
representative of your workload and ensure Node.js/zlib will handle it with
|
||||
acceptable performance and memory usage.
|
||||
|
||||
Tuning of permessage-deflate can be done via the options defined below. You can
|
||||
also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly
|
||||
into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].
|
||||
|
||||
See [the docs][ws-server-options] for more options.
|
||||
|
||||
```js
|
||||
import WebSocket, { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({
|
||||
port: 8080,
|
||||
perMessageDeflate: {
|
||||
zlibDeflateOptions: {
|
||||
// See zlib defaults.
|
||||
chunkSize: 1024,
|
||||
memLevel: 7,
|
||||
level: 3
|
||||
},
|
||||
zlibInflateOptions: {
|
||||
chunkSize: 10 * 1024
|
||||
},
|
||||
// Other options settable:
|
||||
clientNoContextTakeover: true, // Defaults to negotiated value.
|
||||
serverNoContextTakeover: true, // Defaults to negotiated value.
|
||||
serverMaxWindowBits: 10, // Defaults to negotiated value.
|
||||
// Below options specified as default values.
|
||||
concurrencyLimit: 10, // Limits zlib concurrency for perf.
|
||||
threshold: 1024 // Size (in bytes) below which messages
|
||||
// should not be compressed if context takeover is disabled.
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The client will only use the extension if it is supported and enabled on the
|
||||
server. To always disable the extension on the client, set the
|
||||
`perMessageDeflate` option to `false`.
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path', {
|
||||
perMessageDeflate: false
|
||||
});
|
||||
```
|
||||
|
||||
## Usage examples
|
||||
|
||||
### Sending and receiving text data
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path');
|
||||
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('open', function open() {
|
||||
ws.send('something');
|
||||
});
|
||||
|
||||
ws.on('message', function message(data) {
|
||||
console.log('received: %s', data);
|
||||
});
|
||||
```
|
||||
|
||||
### Sending binary data
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path');
|
||||
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('open', function open() {
|
||||
const array = new Float32Array(5);
|
||||
|
||||
for (var i = 0; i < array.length; ++i) {
|
||||
array[i] = i / 2;
|
||||
}
|
||||
|
||||
ws.send(array);
|
||||
});
|
||||
```
|
||||
|
||||
### Simple server
|
||||
|
||||
```js
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('message', function message(data) {
|
||||
console.log('received: %s', data);
|
||||
});
|
||||
|
||||
ws.send('something');
|
||||
});
|
||||
```
|
||||
|
||||
### External HTTP/S server
|
||||
|
||||
```js
|
||||
import { createServer } from 'https';
|
||||
import { readFileSync } from 'fs';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const server = createServer({
|
||||
cert: readFileSync('/path/to/cert.pem'),
|
||||
key: readFileSync('/path/to/key.pem')
|
||||
});
|
||||
const wss = new WebSocketServer({ server });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('message', function message(data) {
|
||||
console.log('received: %s', data);
|
||||
});
|
||||
|
||||
ws.send('something');
|
||||
});
|
||||
|
||||
server.listen(8080);
|
||||
```
|
||||
|
||||
### Multiple servers sharing a single HTTP/S server
|
||||
|
||||
```js
|
||||
import { createServer } from 'http';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const server = createServer();
|
||||
const wss1 = new WebSocketServer({ noServer: true });
|
||||
const wss2 = new WebSocketServer({ noServer: true });
|
||||
|
||||
wss1.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
// ...
|
||||
});
|
||||
|
||||
wss2.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
// ...
|
||||
});
|
||||
|
||||
server.on('upgrade', function upgrade(request, socket, head) {
|
||||
const { pathname } = new URL(request.url, 'wss://base.url');
|
||||
|
||||
if (pathname === '/foo') {
|
||||
wss1.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wss1.emit('connection', ws, request);
|
||||
});
|
||||
} else if (pathname === '/bar') {
|
||||
wss2.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wss2.emit('connection', ws, request);
|
||||
});
|
||||
} else {
|
||||
socket.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(8080);
|
||||
```
|
||||
|
||||
### Client authentication
|
||||
|
||||
```js
|
||||
import { createServer } from 'http';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
function onSocketError(err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
const server = createServer();
|
||||
const wss = new WebSocketServer({ noServer: true });
|
||||
|
||||
wss.on('connection', function connection(ws, request, client) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('message', function message(data) {
|
||||
console.log(`Received message ${data} from user ${client}`);
|
||||
});
|
||||
});
|
||||
|
||||
server.on('upgrade', function upgrade(request, socket, head) {
|
||||
socket.on('error', onSocketError);
|
||||
|
||||
// This function is not defined on purpose. Implement it with your own logic.
|
||||
authenticate(request, function next(err, client) {
|
||||
if (err || !client) {
|
||||
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
socket.removeListener('error', onSocketError);
|
||||
|
||||
wss.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wss.emit('connection', ws, request, client);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(8080);
|
||||
```
|
||||
|
||||
Also see the provided [example][session-parse-example] using `express-session`.
|
||||
|
||||
### Server broadcast
|
||||
|
||||
A client WebSocket broadcasting to all connected WebSocket clients, including
|
||||
itself.
|
||||
|
||||
```js
|
||||
import WebSocket, { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('message', function message(data, isBinary) {
|
||||
wss.clients.forEach(function each(client) {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(data, { binary: isBinary });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
A client WebSocket broadcasting to every other connected WebSocket clients,
|
||||
excluding itself.
|
||||
|
||||
```js
|
||||
import WebSocket, { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('message', function message(data, isBinary) {
|
||||
wss.clients.forEach(function each(client) {
|
||||
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
||||
client.send(data, { binary: isBinary });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Round-trip time
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('wss://websocket-echo.com/');
|
||||
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('open', function open() {
|
||||
console.log('connected');
|
||||
ws.send(Date.now());
|
||||
});
|
||||
|
||||
ws.on('close', function close() {
|
||||
console.log('disconnected');
|
||||
});
|
||||
|
||||
ws.on('message', function message(data) {
|
||||
console.log(`Round-trip time: ${Date.now() - data} ms`);
|
||||
|
||||
setTimeout(function timeout() {
|
||||
ws.send(Date.now());
|
||||
}, 500);
|
||||
});
|
||||
```
|
||||
|
||||
### Use the Node.js streams API
|
||||
|
||||
```js
|
||||
import WebSocket, { createWebSocketStream } from 'ws';
|
||||
|
||||
const ws = new WebSocket('wss://websocket-echo.com/');
|
||||
|
||||
const duplex = createWebSocketStream(ws, { encoding: 'utf8' });
|
||||
|
||||
duplex.on('error', console.error);
|
||||
|
||||
duplex.pipe(process.stdout);
|
||||
process.stdin.pipe(duplex);
|
||||
```
|
||||
|
||||
### Other examples
|
||||
|
||||
For a full example with a browser client communicating with a ws server, see the
|
||||
examples folder.
|
||||
|
||||
Otherwise, see the test cases.
|
||||
|
||||
## FAQ
|
||||
|
||||
### How to get the IP address of the client?
|
||||
|
||||
The remote IP address can be obtained from the raw socket.
|
||||
|
||||
```js
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws, req) {
|
||||
const ip = req.socket.remoteAddress;
|
||||
|
||||
ws.on('error', console.error);
|
||||
});
|
||||
```
|
||||
|
||||
When the server runs behind a proxy like NGINX, the de-facto standard is to use
|
||||
the `X-Forwarded-For` header.
|
||||
|
||||
```js
|
||||
wss.on('connection', function connection(ws, req) {
|
||||
const ip = req.headers['x-forwarded-for'].split(',')[0].trim();
|
||||
|
||||
ws.on('error', console.error);
|
||||
});
|
||||
```
|
||||
|
||||
### How to detect and close broken connections?
|
||||
|
||||
Sometimes, the link between the server and the client can be interrupted in a
|
||||
way that keeps both the server and the client unaware of the broken state of the
|
||||
connection (e.g. when pulling the cord).
|
||||
|
||||
In these cases, ping messages can be used as a means to verify that the remote
|
||||
endpoint is still responsive.
|
||||
|
||||
```js
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
function heartbeat() {
|
||||
this.isAlive = true;
|
||||
}
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.isAlive = true;
|
||||
ws.on('error', console.error);
|
||||
ws.on('pong', heartbeat);
|
||||
});
|
||||
|
||||
const interval = setInterval(function ping() {
|
||||
wss.clients.forEach(function each(ws) {
|
||||
if (ws.isAlive === false) return ws.terminate();
|
||||
|
||||
ws.isAlive = false;
|
||||
ws.ping();
|
||||
});
|
||||
}, 30000);
|
||||
|
||||
wss.on('close', function close() {
|
||||
clearInterval(interval);
|
||||
});
|
||||
```
|
||||
|
||||
Pong messages are automatically sent in response to ping messages as required by
|
||||
the spec.
|
||||
|
||||
Just like the server example above, your clients might as well lose connection
|
||||
without knowing it. You might want to add a ping listener on your clients to
|
||||
prevent that. A simple implementation would be:
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
function heartbeat() {
|
||||
clearTimeout(this.pingTimeout);
|
||||
|
||||
// Use `WebSocket#terminate()`, which immediately destroys the connection,
|
||||
// instead of `WebSocket#close()`, which waits for the close timer.
|
||||
// Delay should be equal to the interval at which your server
|
||||
// sends out pings plus a conservative assumption of the latency.
|
||||
this.pingTimeout = setTimeout(() => {
|
||||
this.terminate();
|
||||
}, 30000 + 1000);
|
||||
}
|
||||
|
||||
const client = new WebSocket('wss://websocket-echo.com/');
|
||||
|
||||
client.on('error', console.error);
|
||||
client.on('open', heartbeat);
|
||||
client.on('ping', heartbeat);
|
||||
client.on('close', function clear() {
|
||||
clearTimeout(this.pingTimeout);
|
||||
});
|
||||
```
|
||||
|
||||
### How to connect via a proxy?
|
||||
|
||||
Use a custom `http.Agent` implementation like [https-proxy-agent][] or
|
||||
[socks-proxy-agent][].
|
||||
|
||||
## Changelog
|
||||
|
||||
We're using the GitHub [releases][changelog] for changelog entries.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[`buffer.isutf8()`]: https://nodejs.org/api/buffer.html#bufferisutf8input
|
||||
[bufferutil]: https://github.com/websockets/bufferutil
|
||||
[changelog]: https://github.com/websockets/ws/releases
|
||||
[client-report]: http://websockets.github.io/ws/autobahn/clients/
|
||||
[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent
|
||||
[node-zlib-bug]: https://github.com/nodejs/node/issues/8871
|
||||
[node-zlib-deflaterawdocs]:
|
||||
https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options
|
||||
[permessage-deflate]: https://tools.ietf.org/html/rfc7692
|
||||
[server-report]: http://websockets.github.io/ws/autobahn/servers/
|
||||
[session-parse-example]: ./examples/express-session-parse
|
||||
[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
|
||||
[utf-8-validate]: https://github.com/websockets/utf-8-validate
|
||||
[ws-server-options]: ./doc/ws.md#new-websocketserveroptions-callback
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* @fileoverview Rule to disallow if as the only statement in an else block
|
||||
* @author Brandon Mills
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description:
|
||||
"Disallow `if` statements as the only statement in `else` blocks",
|
||||
recommended: false,
|
||||
frozen: true,
|
||||
url: "https://eslint.org/docs/latest/rules/no-lonely-if",
|
||||
},
|
||||
|
||||
schema: [],
|
||||
fixable: "code",
|
||||
|
||||
messages: {
|
||||
unexpectedLonelyIf:
|
||||
"Unexpected if as the only statement in an else block.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
return {
|
||||
IfStatement(node) {
|
||||
const parent = node.parent,
|
||||
grandparent = parent.parent;
|
||||
|
||||
if (
|
||||
parent &&
|
||||
parent.type === "BlockStatement" &&
|
||||
parent.body.length === 1 &&
|
||||
!astUtils.areBracesNecessary(parent, sourceCode) &&
|
||||
grandparent &&
|
||||
grandparent.type === "IfStatement" &&
|
||||
parent === grandparent.alternate
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpectedLonelyIf",
|
||||
fix(fixer) {
|
||||
const openingElseCurly =
|
||||
sourceCode.getFirstToken(parent);
|
||||
const closingElseCurly =
|
||||
sourceCode.getLastToken(parent);
|
||||
const elseKeyword =
|
||||
sourceCode.getTokenBefore(openingElseCurly);
|
||||
const tokenAfterElseBlock =
|
||||
sourceCode.getTokenAfter(closingElseCurly);
|
||||
const lastIfToken = sourceCode.getLastToken(
|
||||
node.consequent,
|
||||
);
|
||||
const sourceText = sourceCode.getText();
|
||||
|
||||
if (
|
||||
sourceText
|
||||
.slice(
|
||||
openingElseCurly.range[1],
|
||||
node.range[0],
|
||||
)
|
||||
.trim() ||
|
||||
sourceText
|
||||
.slice(
|
||||
node.range[1],
|
||||
closingElseCurly.range[0],
|
||||
)
|
||||
.trim()
|
||||
) {
|
||||
// Don't fix if there are any non-whitespace characters interfering (e.g. comments)
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
node.consequent.type !== "BlockStatement" &&
|
||||
lastIfToken.value !== ";" &&
|
||||
tokenAfterElseBlock &&
|
||||
(node.consequent.loc.end.line ===
|
||||
tokenAfterElseBlock.loc.start.line ||
|
||||
/^[([/+`-]/u.test(
|
||||
tokenAfterElseBlock.value,
|
||||
) ||
|
||||
lastIfToken.value === "++" ||
|
||||
lastIfToken.value === "--")
|
||||
) {
|
||||
/*
|
||||
* If the `if` statement has no block, and is not followed by a semicolon, make sure that fixing
|
||||
* the issue would not change semantics due to ASI. If this would happen, don't do a fix.
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
return fixer.replaceTextRange(
|
||||
[
|
||||
openingElseCurly.range[0],
|
||||
closingElseCurly.range[1],
|
||||
],
|
||||
(elseKeyword.range[1] ===
|
||||
openingElseCurly.range[0]
|
||||
? " "
|
||||
: "") + sourceCode.getText(node),
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user