WIP: bootstrap and partial real Solana watcher implementation
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
const BINARY_TYPES = ['nodebuffer', 'arraybuffer', 'fragments'];
|
||||
const hasBlob = typeof Blob !== 'undefined';
|
||||
|
||||
if (hasBlob) BINARY_TYPES.push('blob');
|
||||
|
||||
module.exports = {
|
||||
BINARY_TYPES,
|
||||
CLOSE_TIMEOUT: 30000,
|
||||
EMPTY_BUFFER: Buffer.alloc(0),
|
||||
GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
|
||||
hasBlob,
|
||||
kForOnEventAttribute: Symbol('kIsForOnEventAttribute'),
|
||||
kListener: Symbol('kListener'),
|
||||
kStatusCode: Symbol('status-code'),
|
||||
kWebSocket: Symbol('websocket'),
|
||||
NOOP: () => {}
|
||||
};
|
||||
@@ -0,0 +1,633 @@
|
||||
import { describe, expect, expectTypeOf, test } from "vitest";
|
||||
import * as z from "zod/v4";
|
||||
|
||||
describe("basic refinement functionality", () => {
|
||||
test("should create a new schema instance when refining", () => {
|
||||
const obj1 = z.object({
|
||||
first: z.string(),
|
||||
second: z.string(),
|
||||
});
|
||||
const obj2 = obj1.partial().strict();
|
||||
const obj3 = obj2.refine((data) => data.first || data.second, "Either first or second should be filled in.");
|
||||
|
||||
expect(obj1 === (obj2 as any)).toEqual(false);
|
||||
expect(obj2 === (obj3 as any)).toEqual(false);
|
||||
});
|
||||
|
||||
test("should validate according to refinement logic", () => {
|
||||
const schema = z
|
||||
.object({
|
||||
first: z.string(),
|
||||
second: z.string(),
|
||||
})
|
||||
.partial()
|
||||
.strict()
|
||||
.refine((data) => data.first || data.second, "Either first or second should be filled in.");
|
||||
|
||||
// Should fail on empty object
|
||||
expect(() => schema.parse({})).toThrow();
|
||||
|
||||
// Should pass with first property
|
||||
expect(schema.parse({ first: "a" })).toEqual({ first: "a" });
|
||||
|
||||
// Should pass with second property
|
||||
expect(schema.parse({ second: "a" })).toEqual({ second: "a" });
|
||||
|
||||
// Should pass with both properties
|
||||
expect(schema.parse({ first: "a", second: "a" })).toEqual({ first: "a", second: "a" });
|
||||
});
|
||||
|
||||
test("should validate strict mode correctly", () => {
|
||||
const schema = z
|
||||
.object({
|
||||
first: z.string(),
|
||||
second: z.string(),
|
||||
})
|
||||
.partial()
|
||||
.strict();
|
||||
|
||||
// Should throw on extra properties
|
||||
expect(() => schema.parse({ third: "adsf" })).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("refinement with custom error messages", () => {
|
||||
test("should use custom error message when validation fails", () => {
|
||||
const validationSchema = z
|
||||
.object({
|
||||
email: z.string().email(),
|
||||
password: z.string(),
|
||||
confirmPassword: z.string(),
|
||||
})
|
||||
.refine((data) => data.password === data.confirmPassword, "Both password and confirmation must match");
|
||||
|
||||
const result = validationSchema.safeParse({
|
||||
email: "aaaa@gmail.com",
|
||||
password: "aaaaaaaa",
|
||||
confirmPassword: "bbbbbbbb",
|
||||
});
|
||||
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues[0].message).toEqual("Both password and confirmation must match");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("async refinements", () => {
|
||||
test("should support async refinement functions", async () => {
|
||||
const validationSchema = z
|
||||
.object({
|
||||
email: z.string().email(),
|
||||
password: z.string(),
|
||||
confirmPassword: z.string(),
|
||||
})
|
||||
.refine(
|
||||
(data) => Promise.resolve().then(() => data.password === data.confirmPassword),
|
||||
"Both password and confirmation must match"
|
||||
);
|
||||
|
||||
// Should pass with matching passwords
|
||||
const validData = {
|
||||
email: "aaaa@gmail.com",
|
||||
password: "password",
|
||||
confirmPassword: "password",
|
||||
};
|
||||
|
||||
await expect(validationSchema.parseAsync(validData)).resolves.toEqual(validData);
|
||||
|
||||
// Should fail with non-matching passwords
|
||||
await expect(
|
||||
validationSchema.parseAsync({
|
||||
email: "aaaa@gmail.com",
|
||||
password: "password",
|
||||
confirmPassword: "different",
|
||||
})
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("early termination options", () => {
|
||||
test("should abort early with continue: false", () => {
|
||||
const schema = z
|
||||
.string()
|
||||
.superRefine((val, ctx) => {
|
||||
if (val.length < 2) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: "BAD",
|
||||
continue: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.refine((_) => false);
|
||||
|
||||
const result = schema.safeParse("");
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.length).toEqual(1);
|
||||
expect(result.error.issues[0].message).toEqual("BAD");
|
||||
}
|
||||
});
|
||||
|
||||
test("should abort early with fatal: true", () => {
|
||||
const schema = z
|
||||
.string()
|
||||
.superRefine((val, ctx) => {
|
||||
if (val.length < 2) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
fatal: true,
|
||||
message: "BAD",
|
||||
});
|
||||
}
|
||||
})
|
||||
.refine((_) => false);
|
||||
|
||||
const result = schema.safeParse("");
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.length).toEqual(1);
|
||||
expect(result.error.issues[0].message).toEqual("BAD");
|
||||
}
|
||||
});
|
||||
|
||||
test("should abort early with abort flag", () => {
|
||||
const schema = z
|
||||
.string()
|
||||
.refine((_) => false, { abort: true })
|
||||
.refine((_) => false);
|
||||
|
||||
const result = schema.safeParse("");
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.length).toEqual(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("custom error paths", () => {
|
||||
test("should use custom path in error message", () => {
|
||||
const result = z
|
||||
.object({ password: z.string(), confirm: z.string() })
|
||||
.refine((data) => data.confirm === data.password, { path: ["confirm"] })
|
||||
.safeParse({ password: "asdf", confirm: "qewr" });
|
||||
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues[0].path).toEqual(["confirm"]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("superRefine functionality", () => {
|
||||
test("should support multiple validation rules", () => {
|
||||
const Strings = z.array(z.string()).superRefine((val, ctx) => {
|
||||
if (val.length > 3) {
|
||||
ctx.addIssue({
|
||||
input: val,
|
||||
code: "too_big",
|
||||
origin: "array",
|
||||
maximum: 3,
|
||||
inclusive: true,
|
||||
exact: true,
|
||||
message: "Too many items 😡",
|
||||
});
|
||||
}
|
||||
|
||||
if (val.length !== new Set(val).size) {
|
||||
ctx.addIssue({
|
||||
input: val,
|
||||
code: "custom",
|
||||
message: `No duplicates allowed.`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Should fail with too many items and duplicates
|
||||
const result = Strings.safeParse(["asfd", "asfd", "asfd", "asfd"]);
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.length).toEqual(2);
|
||||
expect(result.error.issues[0].message).toEqual("Too many items 😡");
|
||||
expect(result.error.issues[1].message).toEqual("No duplicates allowed.");
|
||||
}
|
||||
|
||||
// Should pass with valid input
|
||||
const validArray = ["asfd", "qwer"];
|
||||
expect(Strings.parse(validArray)).toEqual(validArray);
|
||||
});
|
||||
|
||||
test("should support async superRefine", async () => {
|
||||
const Strings = z.array(z.string()).superRefine(async (val, ctx) => {
|
||||
if (val.length > 3) {
|
||||
ctx.addIssue({
|
||||
input: val,
|
||||
code: "too_big",
|
||||
origin: "array",
|
||||
maximum: 3,
|
||||
inclusive: true,
|
||||
message: "Too many items 😡",
|
||||
});
|
||||
}
|
||||
|
||||
if (val.length !== new Set(val).size) {
|
||||
ctx.addIssue({
|
||||
input: val,
|
||||
code: "custom",
|
||||
message: `No duplicates allowed.`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Should fail with too many items and duplicates
|
||||
const result = await Strings.safeParseAsync(["asfd", "asfd", "asfd", "asfd"]);
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.length).toEqual(2);
|
||||
}
|
||||
|
||||
// Should pass with valid input
|
||||
const validArray = ["asfd", "qwer"];
|
||||
await expect(Strings.parseAsync(validArray)).resolves.toEqual(validArray);
|
||||
});
|
||||
|
||||
test("should test continuability of custom issues", () => {
|
||||
// Default continue behavior - allows subsequent refinements
|
||||
const defaultContinue = z
|
||||
.string()
|
||||
.superRefine((_, ctx) => {
|
||||
ctx.addIssue({ code: "custom", message: "First issue" });
|
||||
})
|
||||
.refine(() => false, "Second issue");
|
||||
|
||||
expect(defaultContinue.safeParse("test")).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"code": "custom",
|
||||
"message": "First issue",
|
||||
"path": []
|
||||
},
|
||||
{
|
||||
"code": "custom",
|
||||
"path": [],
|
||||
"message": "Second issue"
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
|
||||
// Explicit continue: false - prevents subsequent refinements
|
||||
const explicitContinueFalse = z
|
||||
.string()
|
||||
.superRefine((_, ctx) => {
|
||||
ctx.addIssue({ code: "custom", message: "First issue", continue: false });
|
||||
})
|
||||
.refine(() => false, "Second issue");
|
||||
|
||||
expect(explicitContinueFalse.safeParse("test")).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"code": "custom",
|
||||
"message": "First issue",
|
||||
"path": []
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
|
||||
// Multiple issues in same refinement - both always added regardless of continue
|
||||
const multipleInSame = z.string().superRefine((_, ctx) => {
|
||||
ctx.addIssue({ code: "custom", message: "First", continue: false });
|
||||
ctx.addIssue({ code: "custom", message: "Second" });
|
||||
});
|
||||
|
||||
expect(multipleInSame.safeParse("test")).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"code": "custom",
|
||||
"message": "First",
|
||||
"path": []
|
||||
},
|
||||
{
|
||||
"code": "custom",
|
||||
"message": "Second",
|
||||
"path": []
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("should accept string as shorthand for custom error message", () => {
|
||||
const schema = z.string().superRefine((_, ctx) => {
|
||||
ctx.addIssue("bad stuff");
|
||||
});
|
||||
|
||||
const result = schema.safeParse("asdf");
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues).toHaveLength(1);
|
||||
expect(result.error.issues[0].message).toEqual("bad stuff");
|
||||
}
|
||||
});
|
||||
|
||||
test("should respect fatal flag in superRefine", () => {
|
||||
const schema = z
|
||||
.string()
|
||||
.superRefine((val, ctx) => {
|
||||
if (val === "") {
|
||||
ctx.addIssue({
|
||||
input: val,
|
||||
code: "custom",
|
||||
message: "foo",
|
||||
fatal: true,
|
||||
});
|
||||
}
|
||||
})
|
||||
.superRefine((val, ctx) => {
|
||||
if (val !== " ") {
|
||||
ctx.addIssue({
|
||||
input: val,
|
||||
code: "custom",
|
||||
message: "bar",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const result = schema.safeParse("");
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.length).toEqual(1);
|
||||
expect(result.error.issues[0].message).toEqual("foo");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("chained refinements", () => {
|
||||
test("should collect all validation errors when appropriate", () => {
|
||||
const objectSchema = z
|
||||
.object({
|
||||
length: z.number(),
|
||||
size: z.number(),
|
||||
})
|
||||
.refine(({ length }) => length > 5, {
|
||||
path: ["length"],
|
||||
message: "length greater than 5",
|
||||
})
|
||||
.refine(({ size }) => size > 7, {
|
||||
path: ["size"],
|
||||
message: "size greater than 7",
|
||||
});
|
||||
|
||||
// Should fail with one error
|
||||
const r1 = objectSchema.safeParse({
|
||||
length: 4,
|
||||
size: 9,
|
||||
});
|
||||
expect(r1.success).toEqual(false);
|
||||
if (!r1.success) {
|
||||
expect(r1.error.issues.length).toEqual(1);
|
||||
expect(r1.error.issues[0].path).toEqual(["length"]);
|
||||
}
|
||||
|
||||
// Should fail with two errors
|
||||
const r2 = objectSchema.safeParse({
|
||||
length: 4,
|
||||
size: 3,
|
||||
});
|
||||
expect(r2.success).toEqual(false);
|
||||
if (!r2.success) {
|
||||
expect(r2.error.issues.length).toEqual(2);
|
||||
}
|
||||
|
||||
// Should pass with valid input
|
||||
const validData = {
|
||||
length: 6,
|
||||
size: 8,
|
||||
};
|
||||
expect(objectSchema.parse(validData)).toEqual(validData);
|
||||
});
|
||||
|
||||
test("should run superRefine validation even when base schema validation fails when 'when' is defined and returns true", () => {
|
||||
const baseSchema = z.object({
|
||||
foo: z.number(),
|
||||
bar: z.number(),
|
||||
});
|
||||
|
||||
const schema = baseSchema.superRefine(
|
||||
(data, ctx) => {
|
||||
if (data.foo > 10) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: "foo must be less than 10",
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
when: ({ value }) => baseSchema.pick({ foo: true }).safeParse(value).success,
|
||||
}
|
||||
);
|
||||
|
||||
const result = schema.safeParse({
|
||||
foo: 11,
|
||||
});
|
||||
expect(result.success).toEqual(false);
|
||||
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.length).toEqual(2);
|
||||
expect(result.error.issues[0].message).toEqual("Invalid input: expected number, received undefined");
|
||||
expect(result.error.issues[1].message).toEqual("foo must be less than 10");
|
||||
}
|
||||
});
|
||||
|
||||
test("should not run superRefine validation when 'when' is defined and returns false", () => {
|
||||
const baseSchema = z.object({
|
||||
foo: z.number(),
|
||||
bar: z.number(),
|
||||
});
|
||||
|
||||
const schema = baseSchema.superRefine(
|
||||
(data, ctx) => {
|
||||
if (data.foo > 10) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: "foo must be less than 10",
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
when: ({ value }) => baseSchema.safeParse(value).success,
|
||||
}
|
||||
);
|
||||
|
||||
const result = schema.safeParse({
|
||||
foo: 11,
|
||||
});
|
||||
expect(result.success).toEqual(false);
|
||||
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.length).toEqual(1);
|
||||
expect(result.error.issues[0].message).toEqual("Invalid input: expected number, received undefined");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("type refinement with type guards", () => {
|
||||
test("type guard narrows output type", () => {
|
||||
const schema = z.string().refine((s): s is "a" => s === "a");
|
||||
|
||||
expectTypeOf<z.input<typeof schema>>().toEqualTypeOf<string>();
|
||||
expectTypeOf<z.output<typeof schema>>().toEqualTypeOf<"a">();
|
||||
});
|
||||
|
||||
test("non-type-guard refine does not narrow", () => {
|
||||
const schema = z.string().refine((s) => s.length > 0);
|
||||
|
||||
expectTypeOf<z.input<typeof schema>>().toEqualTypeOf<string>();
|
||||
expectTypeOf<z.output<typeof schema>>().toEqualTypeOf<string>();
|
||||
});
|
||||
|
||||
// TODO: Implement type narrowing for superRefine
|
||||
// test("superRefine - type narrowing", () => {
|
||||
// type NarrowType = { type: string; age: number };
|
||||
// const schema = z
|
||||
// .object({
|
||||
// type: z.string(),
|
||||
// age: z.number(),
|
||||
// })
|
||||
// .nullable()
|
||||
// .superRefine((arg, ctx): arg is NarrowType => {
|
||||
// if (!arg) {
|
||||
// ctx.addIssue({
|
||||
// input: arg,
|
||||
// code: "custom",
|
||||
// message: "cannot be null",
|
||||
// fatal: true,
|
||||
// });
|
||||
// return false;
|
||||
// }
|
||||
// return true;
|
||||
// });
|
||||
//
|
||||
// expectTypeOf<z.infer<typeof schema>>().toEqualTypeOf<NarrowType>();
|
||||
//
|
||||
// expect(schema.safeParse({ type: "test", age: 0 }).success).toEqual(true);
|
||||
// expect(schema.safeParse(null).success).toEqual(false);
|
||||
// });
|
||||
});
|
||||
|
||||
test("when", () => {
|
||||
const schema = z
|
||||
.strictObject({
|
||||
password: z.string().min(8),
|
||||
confirmPassword: z.string(),
|
||||
other: z.string(),
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
// console.log("running check...");
|
||||
// console.log(data);
|
||||
// console.log(data.password);
|
||||
return data.password === data.confirmPassword;
|
||||
},
|
||||
{
|
||||
message: "Passwords do not match",
|
||||
path: ["confirmPassword"],
|
||||
when(payload) {
|
||||
if (payload.value === undefined) return false;
|
||||
if (payload.value === null) return false;
|
||||
// no issues with confirmPassword or password
|
||||
return payload.issues.every((iss) => iss.path?.[0] !== "confirmPassword" && iss.path?.[0] !== "password");
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(schema.safeParse(undefined)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"expected": "object",
|
||||
"code": "invalid_type",
|
||||
"path": [],
|
||||
"message": "Invalid input: expected object, received undefined"
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
expect(schema.safeParse(null)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"expected": "object",
|
||||
"code": "invalid_type",
|
||||
"path": [],
|
||||
"message": "Invalid input: expected object, received null"
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
expect(
|
||||
schema.safeParse({
|
||||
password: "asdf",
|
||||
confirmPassword: "asdfg",
|
||||
other: "qwer",
|
||||
})
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"origin": "string",
|
||||
"code": "too_small",
|
||||
"minimum": 8,
|
||||
"inclusive": true,
|
||||
"path": [
|
||||
"password"
|
||||
],
|
||||
"message": "Too small: expected string to have >=8 characters"
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
|
||||
expect(
|
||||
schema.safeParse({
|
||||
password: "asdf",
|
||||
confirmPassword: "asdfg",
|
||||
other: 1234,
|
||||
})
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"origin": "string",
|
||||
"code": "too_small",
|
||||
"minimum": 8,
|
||||
"inclusive": true,
|
||||
"path": [
|
||||
"password"
|
||||
],
|
||||
"message": "Too small: expected string to have >=8 characters"
|
||||
},
|
||||
{
|
||||
"expected": "string",
|
||||
"code": "invalid_type",
|
||||
"path": [
|
||||
"other"
|
||||
],
|
||||
"message": "Invalid input: expected string, received number"
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import { expectType, expectAssignable } from "tsd";
|
||||
import slowRedact from ".";
|
||||
import type { redactFn, redactFnNoSerialize } from ".";
|
||||
|
||||
// should return redactFn
|
||||
expectType<redactFn>(slowRedact());
|
||||
expectType<redactFn>(slowRedact({ paths: [] }));
|
||||
expectType<redactFn>(slowRedact({ paths: ["some.path"] }));
|
||||
expectType<redactFn>(slowRedact({ paths: [], censor: "[REDACTED]" }));
|
||||
expectType<redactFn>(slowRedact({ paths: [], strict: true }));
|
||||
expectType<redactFn>(slowRedact({ paths: [], serialize: JSON.stringify }));
|
||||
expectType<redactFn>(slowRedact({ paths: [], serialize: true }));
|
||||
expectType<redactFnNoSerialize>(slowRedact({ paths: [], serialize: false }));
|
||||
expectType<redactFn>(slowRedact({ paths: [], remove: true }));
|
||||
|
||||
// should return string
|
||||
expectType<string>(slowRedact()(""));
|
||||
|
||||
// should return string or T
|
||||
expectAssignable<string | { someField: string }>(
|
||||
slowRedact()({ someField: "someValue" })
|
||||
);
|
||||
@@ -0,0 +1,4 @@
|
||||
import * as z from "./external.cjs";
|
||||
export { z };
|
||||
export * from "./external.cjs";
|
||||
export default z;
|
||||
@@ -0,0 +1,5 @@
|
||||
import assertClassBrand from "./assertClassBrand.js";
|
||||
function _classStaticPrivateMethodGet(s, a, t) {
|
||||
return assertClassBrand(a, s), t;
|
||||
}
|
||||
export { _classStaticPrivateMethodGet as default };
|
||||
@@ -0,0 +1,7 @@
|
||||
/* eslint-disable ts/ban-ts-comment */
|
||||
|
||||
// @ts-ignore optional peer dep
|
||||
export type * as jsdomTypes from 'jsdom'
|
||||
|
||||
// @ts-ignore optional peer dep
|
||||
export type * as happyDomTypes from 'happy-dom'
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @fileoverview Types for the config-array package.
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
/**
|
||||
* A file matcher used in `files` and `ignores`.
|
||||
*/
|
||||
export type FileMatcher = string | ((filePath: string) => boolean);
|
||||
/**
|
||||
* An entry in a config's `files` array.
|
||||
*
|
||||
* A subarray means all matchers must match.
|
||||
*/
|
||||
export type FilesMatcher = FileMatcher | FileMatcher[];
|
||||
/**
|
||||
* The config types allowed in the `extraConfigTypes` option.
|
||||
*/
|
||||
export type ExtraConfigType = "array" | "function";
|
||||
export interface ConfigObject {
|
||||
/**
|
||||
* The base path for files and ignores.
|
||||
*/
|
||||
basePath?: string;
|
||||
/**
|
||||
* The files to include.
|
||||
*/
|
||||
files?: FilesMatcher[];
|
||||
/**
|
||||
* The files to exclude.
|
||||
*/
|
||||
ignores?: FileMatcher[];
|
||||
/**
|
||||
* The name of the config object.
|
||||
*/
|
||||
name?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @fileoverview Rule to check for ambiguous div operator in regexes
|
||||
* @author Matt DuVall <http://www.mattduvall.com>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description:
|
||||
"Disallow equal signs explicitly at the beginning of regular expressions",
|
||||
recommended: false,
|
||||
frozen: true,
|
||||
url: "https://eslint.org/docs/latest/rules/no-div-regex",
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
unexpected:
|
||||
"A regular expression literal can be confused with '/='.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
return {
|
||||
Literal(node) {
|
||||
const token = sourceCode.getFirstToken(node);
|
||||
|
||||
if (
|
||||
token.type === "RegularExpression" &&
|
||||
token.value[1] === "="
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpected",
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange(
|
||||
[token.range[0] + 1, token.range[0] + 2],
|
||||
"[=]",
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
// THIS CODE WAS AUTOMATICALLY GENERATED
|
||||
// DO NOT EDIT THIS CODE BY HAND
|
||||
// RUN THE FOLLOWING COMMAND FROM THE WORKSPACE ROOT TO REGENERATE:
|
||||
// npx nx generate-lib repo
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.esnext_weakref = void 0;
|
||||
const base_config_1 = require("./base-config");
|
||||
const es2015_symbol_wellknown_1 = require("./es2015.symbol.wellknown");
|
||||
exports.esnext_weakref = {
|
||||
libs: [es2015_symbol_wellknown_1.es2015_symbol_wellknown],
|
||||
variables: [
|
||||
['WeakRef', base_config_1.TYPE_VALUE],
|
||||
['WeakRefConstructor', base_config_1.TYPE],
|
||||
['FinalizationRegistry', base_config_1.TYPE_VALUE],
|
||||
['FinalizationRegistryConstructor', base_config_1.TYPE],
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { TSESLint } from '@typescript-eslint/utils';
|
||||
export declare const phrases: {
|
||||
readonly TSInterfaceDeclaration: 'Interface';
|
||||
readonly TSTypeLiteral: 'Type literal';
|
||||
};
|
||||
declare const _default: TSESLint.RuleModule<"functionTypeOverCallableType" | "unexpectedThisOnFunctionOnlyInterface", [], import("../../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
|
||||
name: string;
|
||||
};
|
||||
export default _default;
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
export { _ as default } from "../esm/_object_without_properties_loose.js";
|
||||
@@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'],
|
||||
GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
|
||||
kStatusCode: Symbol('status-code'),
|
||||
kWebSocket: Symbol('websocket'),
|
||||
EMPTY_BUFFER: Buffer.alloc(0),
|
||||
NOOP: () => {}
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
export * from './dist/manager.js'
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,12 @@
|
||||
{{# def.definitions }}
|
||||
{{# def.errors }}
|
||||
{{# def.setupKeyword }}
|
||||
{{# def.$data }}
|
||||
|
||||
{{# def.numberKeyword }}
|
||||
|
||||
{{ var $op = $keyword == 'maxLength' ? '>' : '<'; }}
|
||||
if ({{# def.$dataNotType:'number' }} {{# def.strLength }} {{=$op}} {{=$schemaValue}}) {
|
||||
{{ var $errorKeyword = $keyword; }}
|
||||
{{# def.error:'_limitLength' }}
|
||||
} {{? $breakOnError }} else { {{?}}
|
||||
@@ -0,0 +1,323 @@
|
||||
# tinyexec 📟
|
||||
|
||||
> A minimal package for executing commands
|
||||
|
||||
This package was created to provide a minimal way of interacting with child
|
||||
processes without having to manually deal with streams, piping, etc.
|
||||
|
||||
## Installing
|
||||
|
||||
```sh
|
||||
$ npm i -S tinyexec
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
A process can be spawned and awaited like so:
|
||||
|
||||
```ts
|
||||
import {x} from 'tinyexec';
|
||||
|
||||
const result = await x('ls', ['-l']);
|
||||
|
||||
// result.stdout - the stdout as a string
|
||||
// result.stderr - the stderr as a string
|
||||
// result.exitCode - the process exit code as a number
|
||||
```
|
||||
|
||||
By default, tinyexec does not throw on non‑zero exit codes. Check `result.exitCode` or pass `{throwOnError: true}`.
|
||||
|
||||
Output is returned exactly as produced; trailing newlines are not trimmed. If you need trimming, do it explicitly:
|
||||
|
||||
```ts
|
||||
const clean = result.stdout.replace(/\r?\n$/, '');
|
||||
```
|
||||
|
||||
You may also iterate over the lines of output via an async loop:
|
||||
|
||||
```ts
|
||||
import {x} from 'tinyexec';
|
||||
|
||||
const proc = x('ls', ['-l']);
|
||||
|
||||
for await (const line of proc) {
|
||||
// line will be from stderr/stdout in the order you'd see it in a term
|
||||
}
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
Options can be passed to have finer control over spawning of the process:
|
||||
|
||||
```ts
|
||||
await x('ls', [], {
|
||||
timeout: 1000
|
||||
});
|
||||
```
|
||||
|
||||
The options object can have the following properties:
|
||||
|
||||
- `signal` - an `AbortSignal` to allow aborting of the execution
|
||||
- `timeout` - time in milliseconds at which the process will be forcibly killed
|
||||
- `persist` - if `true`, the process will continue after the host exits
|
||||
- `stdin` - `string` or another `Result` that will be used as the input to the process
|
||||
- `nodeOptions` - any valid options to node's underlying `spawn` function
|
||||
- `throwOnError` - if true, non-zero exit codes will throw an error
|
||||
- `nodePath` - if `false`, `node_modules/.bin` directories and the current node executable's directory will not be prepended to `PATH` (defaults to `true`)
|
||||
|
||||
### Passing a string to stdin
|
||||
|
||||
You can pass a string to `stdin`, which is useful for whitespace-sensitive values and for secrets that shouldn’t be exposed in shell history:
|
||||
|
||||
```ts
|
||||
const result = await x('gh', ['auth', 'login', '--with-token'], {
|
||||
stdin: process.env.GITHUB_TOKEN
|
||||
});
|
||||
|
||||
console.log(result.exitCode);
|
||||
```
|
||||
|
||||
### Piping to another process
|
||||
|
||||
You can pipe a process to another via the `pipe` method:
|
||||
|
||||
```ts
|
||||
const proc1 = x('ls', ['-l']);
|
||||
const proc2 = proc1.pipe('grep', ['.js']);
|
||||
const result = await proc2;
|
||||
|
||||
console.log(result.stdout);
|
||||
```
|
||||
|
||||
`pipe` takes the same options as a regular execution. For example, you can
|
||||
pass a timeout to the pipe call:
|
||||
|
||||
```ts
|
||||
proc1.pipe('grep', ['.js'], {
|
||||
timeout: 2000
|
||||
});
|
||||
```
|
||||
|
||||
### Killing a process
|
||||
|
||||
You can kill the process via the `kill` method:
|
||||
|
||||
```ts
|
||||
const proc = x('ls');
|
||||
|
||||
proc.kill();
|
||||
|
||||
// or with a signal
|
||||
proc.kill('SIGHUP');
|
||||
```
|
||||
|
||||
### Node modules/binaries
|
||||
|
||||
By default, node's available binaries from `node_modules` will be accessible
|
||||
in your command.
|
||||
|
||||
For example, in a repo which has `eslint` installed:
|
||||
|
||||
```ts
|
||||
await x('eslint', ['.']);
|
||||
```
|
||||
|
||||
In this example, `eslint` will come from the locally installed `node_modules`.
|
||||
|
||||
If you'd rather not have `node_modules/.bin` (or the directory of the current
|
||||
`node` executable) prepended to `PATH`, pass `nodePath: false`:
|
||||
|
||||
```ts
|
||||
await x('eslint', ['.'], {nodePath: false});
|
||||
```
|
||||
|
||||
### Using an abort signal
|
||||
|
||||
An abort signal can be passed to a process in order to abort it at a later
|
||||
time. This will result in the process being killed and `aborted` being set
|
||||
to `true`.
|
||||
|
||||
```ts
|
||||
const aborter = new AbortController();
|
||||
const proc = x('node', ['./foo.mjs'], {
|
||||
signal: aborter.signal
|
||||
});
|
||||
|
||||
// elsewhere...
|
||||
aborter.abort();
|
||||
|
||||
await proc;
|
||||
|
||||
proc.aborted; // true
|
||||
proc.killed; // true
|
||||
```
|
||||
|
||||
### Using with command strings
|
||||
|
||||
If you need to continue supporting commands as strings (e.g. "command arg0 arg1"),
|
||||
you can use [args-tokenizer](https://github.com/TrySound/args-tokenizer),
|
||||
a lightweight library for parsing shell command strings into an array.
|
||||
|
||||
```ts
|
||||
import {x} from 'tinyexec';
|
||||
import {tokenizeArgs} from 'args-tokenizer';
|
||||
|
||||
const commandString = 'echo "Hello, World!"';
|
||||
const [command, ...args] = tokenizeArgs(commandString);
|
||||
const result = await x(command, args);
|
||||
|
||||
result.stdout; // Hello, World!
|
||||
```
|
||||
|
||||
### Synchronous
|
||||
|
||||
You can use `xSync` for synchronous (blocking) execution:
|
||||
|
||||
```ts
|
||||
import {xSync} from 'tinyexec';
|
||||
|
||||
const result = xSync('ls', ['-l']);
|
||||
|
||||
// result.stdout - the stdout as a string
|
||||
// result.stderr - the stderr as a string
|
||||
// result.exitCode - the process exit code as a number
|
||||
```
|
||||
|
||||
Like the async API, you can iterate over lines:
|
||||
|
||||
```ts
|
||||
const result = xSync('ls', ['-l']);
|
||||
|
||||
for (const line of result) {
|
||||
// line will be from stdout then stderr
|
||||
}
|
||||
```
|
||||
|
||||
Since the synchronous API blocks the event loop, there are some features that are supported in the async API that the sync API does not support:
|
||||
|
||||
- `signal`
|
||||
- `persist`
|
||||
- `kill()` method
|
||||
- `stdin` piping
|
||||
- `pipe()` method
|
||||
|
||||
Other options like `timeout`, `throwOnError`, and `nodeOptions` work the same way.
|
||||
|
||||
## API
|
||||
|
||||
Calling `x(command[, args])` returns an awaitable `Result` which has the
|
||||
following API methods and properties available:
|
||||
|
||||
### `pipe(command[, args[, options]])`
|
||||
|
||||
Pipes the current command to another. For example:
|
||||
|
||||
```ts
|
||||
x('ls', ['-l'])
|
||||
.pipe('grep', ['js']);
|
||||
```
|
||||
|
||||
The parameters are as follows:
|
||||
|
||||
- `command` - the command to execute (_without any arguments_)
|
||||
- `args` - an array of arguments
|
||||
- `options` - options object
|
||||
|
||||
### `process`
|
||||
|
||||
The underlying Node.js `ChildProcess`. tinyexec keeps the surface minimal and does not re‑expose every child_process method/event. Use `proc.process` for advanced access (streams, events, etc.).
|
||||
|
||||
```ts
|
||||
const proc = x('node', ['./foo.mjs']);
|
||||
|
||||
proc.process?.stdout?.on('data', (chunk) => {
|
||||
// ...
|
||||
});
|
||||
proc.process?.once('close', (code) => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### `kill([signal])`
|
||||
|
||||
Kills the current process with the specified signal. By default, this will
|
||||
use the `SIGTERM` signal.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const proc = x('ls');
|
||||
|
||||
proc.kill();
|
||||
```
|
||||
|
||||
### `pid`
|
||||
|
||||
The current process ID. For example:
|
||||
|
||||
```ts
|
||||
const proc = x('ls');
|
||||
|
||||
proc.pid; // number
|
||||
```
|
||||
|
||||
### `aborted`
|
||||
|
||||
Whether the process has been aborted or not (via the `signal` originally
|
||||
passed in the options object).
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const proc = x('ls');
|
||||
|
||||
proc.aborted; // bool
|
||||
```
|
||||
|
||||
### `killed`
|
||||
|
||||
Whether the process has been killed or not (e.g. via `kill()` or an abort
|
||||
signal).
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const proc = x('ls');
|
||||
|
||||
proc.killed; // bool
|
||||
```
|
||||
|
||||
### `exitCode`
|
||||
|
||||
The exit code received when the process completed execution.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const proc = x('ls');
|
||||
|
||||
proc.exitCode; // number (e.g. 1)
|
||||
```
|
||||
|
||||
## Comparison with other libraries
|
||||
|
||||
`tinyexec` aims to provide a lightweight layer on top of Node's own
|
||||
`child_process` API.
|
||||
|
||||
Some clear benefits compared to other libraries are that `tinyexec` will be much lighter, have a much
|
||||
smaller footprint and will have a less abstract interface (less "magic"). It
|
||||
will also have equal security and cross-platform support to popular
|
||||
alternatives.
|
||||
|
||||
There are various features other libraries include which we are unlikely
|
||||
to ever implement, as they would prevent us from providing a lightweight layer.
|
||||
|
||||
For example, if you'd like write scripts rather than individual commands, and
|
||||
prefer to use templating, we'd definitely recommend
|
||||
[zx](https://github.com/google/zx). zx is a much higher level library which
|
||||
does some of the same work `tinyexec` does but behind a template string
|
||||
interface.
|
||||
|
||||
Similarly, libraries like `execa` will provide helpers for various things
|
||||
like passing files as input to processes. We opt not to support features like
|
||||
this since many of them are easy to do yourself (using Node's own APIs).
|
||||
@@ -0,0 +1,29 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = joinLinesWithIndentation
|
||||
|
||||
/**
|
||||
* @typedef {object} JoinLinesWithIndentationParams
|
||||
* @property {string} input The string to split and reformat.
|
||||
* @property {string} [ident] The indentation string. Default: ` ` (4 spaces).
|
||||
* @property {string} [eol] The end of line sequence to use when rejoining
|
||||
* the lines. Default: `'\n'`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Given a string with line separators, either `\r\n` or `\n`, add indentation
|
||||
* to all lines subsequent to the first line and rejoin the lines using an
|
||||
* end of line sequence.
|
||||
*
|
||||
* @param {JoinLinesWithIndentationParams} input
|
||||
*
|
||||
* @returns {string} A string with lines subsequent to the first indented
|
||||
* with the given indentation sequence.
|
||||
*/
|
||||
function joinLinesWithIndentation ({ input, ident = ' ', eol = '\n' }) {
|
||||
const lines = input.split(/\r?\n/)
|
||||
for (let i = 1; i < lines.length; i += 1) {
|
||||
lines[i] = ident + lines[i]
|
||||
}
|
||||
return lines.join(eol)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { _ as default } from "../esm/_extends.js";
|
||||
@@ -0,0 +1,322 @@
|
||||
/*! *****************************************************************************
|
||||
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"/>
|
||||
|
||||
/////////////////////////////
|
||||
/// Windows Script Host APIS
|
||||
/////////////////////////////
|
||||
|
||||
interface ActiveXObject {
|
||||
new (s: string): any;
|
||||
}
|
||||
declare var ActiveXObject: ActiveXObject;
|
||||
|
||||
interface ITextWriter {
|
||||
Write(s: string): void;
|
||||
WriteLine(s: string): void;
|
||||
Close(): void;
|
||||
}
|
||||
|
||||
interface TextStreamBase {
|
||||
/**
|
||||
* The column number of the current character position in an input stream.
|
||||
*/
|
||||
Column: number;
|
||||
|
||||
/**
|
||||
* The current line number in an input stream.
|
||||
*/
|
||||
Line: number;
|
||||
|
||||
/**
|
||||
* Closes a text stream.
|
||||
* It is not necessary to close standard streams; they close automatically when the process ends. If
|
||||
* you close a standard stream, be aware that any other pointers to that standard stream become invalid.
|
||||
*/
|
||||
Close(): void;
|
||||
}
|
||||
|
||||
interface TextStreamWriter extends TextStreamBase {
|
||||
/**
|
||||
* Sends a string to an output stream.
|
||||
*/
|
||||
Write(s: string): void;
|
||||
|
||||
/**
|
||||
* Sends a specified number of blank lines (newline characters) to an output stream.
|
||||
*/
|
||||
WriteBlankLines(intLines: number): void;
|
||||
|
||||
/**
|
||||
* Sends a string followed by a newline character to an output stream.
|
||||
*/
|
||||
WriteLine(s: string): void;
|
||||
}
|
||||
|
||||
interface TextStreamReader extends TextStreamBase {
|
||||
/**
|
||||
* Returns a specified number of characters from an input stream, starting at the current pointer position.
|
||||
* Does not return until the ENTER key is pressed.
|
||||
* Can only be used on a stream in reading mode; causes an error in writing or appending mode.
|
||||
*/
|
||||
Read(characters: number): string;
|
||||
|
||||
/**
|
||||
* Returns all characters from an input stream.
|
||||
* Can only be used on a stream in reading mode; causes an error in writing or appending mode.
|
||||
*/
|
||||
ReadAll(): string;
|
||||
|
||||
/**
|
||||
* Returns an entire line from an input stream.
|
||||
* Although this method extracts the newline character, it does not add it to the returned string.
|
||||
* Can only be used on a stream in reading mode; causes an error in writing or appending mode.
|
||||
*/
|
||||
ReadLine(): string;
|
||||
|
||||
/**
|
||||
* Skips a specified number of characters when reading from an input text stream.
|
||||
* Can only be used on a stream in reading mode; causes an error in writing or appending mode.
|
||||
* @param characters Positive number of characters to skip forward. (Backward skipping is not supported.)
|
||||
*/
|
||||
Skip(characters: number): void;
|
||||
|
||||
/**
|
||||
* Skips the next line when reading from an input text stream.
|
||||
* Can only be used on a stream in reading mode, not writing or appending mode.
|
||||
*/
|
||||
SkipLine(): void;
|
||||
|
||||
/**
|
||||
* Indicates whether the stream pointer position is at the end of a line.
|
||||
*/
|
||||
AtEndOfLine: boolean;
|
||||
|
||||
/**
|
||||
* Indicates whether the stream pointer position is at the end of a stream.
|
||||
*/
|
||||
AtEndOfStream: boolean;
|
||||
}
|
||||
|
||||
declare var WScript: {
|
||||
/**
|
||||
* Outputs text to either a message box (under WScript.exe) or the command console window followed by
|
||||
* a newline (under CScript.exe).
|
||||
*/
|
||||
Echo(s: any): void;
|
||||
|
||||
/**
|
||||
* Exposes the write-only error output stream for the current script.
|
||||
* Can be accessed only while using CScript.exe.
|
||||
*/
|
||||
StdErr: TextStreamWriter;
|
||||
|
||||
/**
|
||||
* Exposes the write-only output stream for the current script.
|
||||
* Can be accessed only while using CScript.exe.
|
||||
*/
|
||||
StdOut: TextStreamWriter;
|
||||
Arguments: { length: number; Item(n: number): string; };
|
||||
|
||||
/**
|
||||
* The full path of the currently running script.
|
||||
*/
|
||||
ScriptFullName: string;
|
||||
|
||||
/**
|
||||
* Forces the script to stop immediately, with an optional exit code.
|
||||
*/
|
||||
Quit(exitCode?: number): number;
|
||||
|
||||
/**
|
||||
* The Windows Script Host build version number.
|
||||
*/
|
||||
BuildVersion: number;
|
||||
|
||||
/**
|
||||
* Fully qualified path of the host executable.
|
||||
*/
|
||||
FullName: string;
|
||||
|
||||
/**
|
||||
* Gets/sets the script mode - interactive(true) or batch(false).
|
||||
*/
|
||||
Interactive: boolean;
|
||||
|
||||
/**
|
||||
* The name of the host executable (WScript.exe or CScript.exe).
|
||||
*/
|
||||
Name: string;
|
||||
|
||||
/**
|
||||
* Path of the directory containing the host executable.
|
||||
*/
|
||||
Path: string;
|
||||
|
||||
/**
|
||||
* The filename of the currently running script.
|
||||
*/
|
||||
ScriptName: string;
|
||||
|
||||
/**
|
||||
* Exposes the read-only input stream for the current script.
|
||||
* Can be accessed only while using CScript.exe.
|
||||
*/
|
||||
StdIn: TextStreamReader;
|
||||
|
||||
/**
|
||||
* Windows Script Host version
|
||||
*/
|
||||
Version: string;
|
||||
|
||||
/**
|
||||
* Connects a COM object's event sources to functions named with a given prefix, in the form prefix_event.
|
||||
*/
|
||||
ConnectObject(objEventSource: any, strPrefix: string): void;
|
||||
|
||||
/**
|
||||
* Creates a COM object.
|
||||
* @param strProgiID
|
||||
* @param strPrefix Function names in the form prefix_event will be bound to this object's COM events.
|
||||
*/
|
||||
CreateObject(strProgID: string, strPrefix?: string): any;
|
||||
|
||||
/**
|
||||
* Disconnects a COM object from its event sources.
|
||||
*/
|
||||
DisconnectObject(obj: any): void;
|
||||
|
||||
/**
|
||||
* Retrieves an existing object with the specified ProgID from memory, or creates a new one from a file.
|
||||
* @param strPathname Fully qualified path to the file containing the object persisted to disk.
|
||||
* For objects in memory, pass a zero-length string.
|
||||
* @param strProgID
|
||||
* @param strPrefix Function names in the form prefix_event will be bound to this object's COM events.
|
||||
*/
|
||||
GetObject(strPathname: string, strProgID?: string, strPrefix?: string): any;
|
||||
|
||||
/**
|
||||
* Suspends script execution for a specified length of time, then continues execution.
|
||||
* @param intTime Interval (in milliseconds) to suspend script execution.
|
||||
*/
|
||||
Sleep(intTime: number): void;
|
||||
};
|
||||
|
||||
/**
|
||||
* WSH is an alias for WScript under Windows Script Host
|
||||
*/
|
||||
declare var WSH: typeof WScript;
|
||||
|
||||
/**
|
||||
* Represents an Automation SAFEARRAY
|
||||
*/
|
||||
declare class SafeArray<T = any> {
|
||||
private constructor();
|
||||
private SafeArray_typekey: SafeArray<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows enumerating over a COM collection, which may not have indexed item access.
|
||||
*/
|
||||
interface Enumerator<T = any> {
|
||||
/**
|
||||
* Returns true if the current item is the last one in the collection, or the collection is empty,
|
||||
* or the current item is undefined.
|
||||
*/
|
||||
atEnd(): boolean;
|
||||
|
||||
/**
|
||||
* Returns the current item in the collection
|
||||
*/
|
||||
item(): T;
|
||||
|
||||
/**
|
||||
* Resets the current item in the collection to the first item. If there are no items in the collection,
|
||||
* the current item is set to undefined.
|
||||
*/
|
||||
moveFirst(): void;
|
||||
|
||||
/**
|
||||
* Moves the current item to the next item in the collection. If the enumerator is at the end of
|
||||
* the collection or the collection is empty, the current item is set to undefined.
|
||||
*/
|
||||
moveNext(): void;
|
||||
}
|
||||
|
||||
interface EnumeratorConstructor {
|
||||
new <T = any>(safearray: SafeArray<T>): Enumerator<T>;
|
||||
new <T = any>(collection: { Item(index: any): T; }): Enumerator<T>;
|
||||
new <T = any>(collection: any): Enumerator<T>;
|
||||
}
|
||||
|
||||
declare var Enumerator: EnumeratorConstructor;
|
||||
|
||||
/**
|
||||
* Enables reading from a COM safe array, which might have an alternate lower bound, or multiple dimensions.
|
||||
*/
|
||||
interface VBArray<T = any> {
|
||||
/**
|
||||
* Returns the number of dimensions (1-based).
|
||||
*/
|
||||
dimensions(): number;
|
||||
|
||||
/**
|
||||
* Takes an index for each dimension in the array, and returns the item at the corresponding location.
|
||||
*/
|
||||
getItem(dimension1Index: number, ...dimensionNIndexes: number[]): T;
|
||||
|
||||
/**
|
||||
* Returns the smallest available index for a given dimension.
|
||||
* @param dimension 1-based dimension (defaults to 1)
|
||||
*/
|
||||
lbound(dimension?: number): number;
|
||||
|
||||
/**
|
||||
* Returns the largest available index for a given dimension.
|
||||
* @param dimension 1-based dimension (defaults to 1)
|
||||
*/
|
||||
ubound(dimension?: number): number;
|
||||
|
||||
/**
|
||||
* Returns a Javascript array with all the elements in the VBArray. If there are multiple dimensions,
|
||||
* each successive dimension is appended to the end of the array.
|
||||
* Example: [[1,2,3],[4,5,6]] becomes [1,2,3,4,5,6]
|
||||
*/
|
||||
toArray(): T[];
|
||||
}
|
||||
|
||||
interface VBArrayConstructor {
|
||||
new <T = any>(safeArray: SafeArray<T>): VBArray<T>;
|
||||
}
|
||||
|
||||
declare var VBArray: VBArrayConstructor;
|
||||
|
||||
/**
|
||||
* Automation date (VT_DATE)
|
||||
*/
|
||||
declare class VarDate {
|
||||
private constructor();
|
||||
private VarDate_typekey: VarDate;
|
||||
}
|
||||
|
||||
interface DateConstructor {
|
||||
new (vd: VarDate): Date;
|
||||
}
|
||||
|
||||
interface Date {
|
||||
getVarDate: () => VarDate;
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
export type Schema =
|
||||
| ObjectSchema
|
||||
| ArraySchema
|
||||
| StringSchema
|
||||
| NumberSchema
|
||||
| IntegerSchema
|
||||
| BooleanSchema
|
||||
| NullSchema;
|
||||
|
||||
// export type JsonType = "object" | "array" | "string" | "number" | "boolean" | "null" | "integer";
|
||||
|
||||
// export interface JSONSchema {
|
||||
// type?: string ;
|
||||
// $id?: string ;
|
||||
// id?: string ;
|
||||
// $schema?: string ;
|
||||
// $ref?: string ;
|
||||
// $anchor?: string ;
|
||||
// $defs?: { [key: string]: JSONSchema } ;
|
||||
// definitions?: { [key: string]: JSONSchema } ;
|
||||
// $comment?: string ;
|
||||
// title?: string ;
|
||||
// description?: string ;
|
||||
// default?: unknown ;
|
||||
// examples?: unknown[] ;
|
||||
// readOnly?: boolean ;
|
||||
// writeOnly?: boolean ;
|
||||
// deprecated?: boolean ;
|
||||
// allOf?: JSONSchema[] ;
|
||||
// anyOf?: JSONSchema[] ;
|
||||
// oneOf?: JSONSchema[] ;
|
||||
// not?: JSONSchema ;
|
||||
// if?: JSONSchema ;
|
||||
// then?: JSONSchema ;
|
||||
// else?: JSONSchema ;
|
||||
// enum?: Array<string | number | boolean | null> ;
|
||||
// const?: string | number | boolean | null ;
|
||||
// [k: string]: unknown;
|
||||
|
||||
// /** A special key used as an intermediate representation of extends-style relationships. Omitted as a $ref with additional properties. */
|
||||
// // _ref?: JSONSchema;
|
||||
// _prefault?: unknown ;
|
||||
// }
|
||||
|
||||
export type _JSONSchema = boolean | JSONSchema;
|
||||
export type JSONSchema = {
|
||||
[k: string]: unknown;
|
||||
$schema?:
|
||||
| "https://json-schema.org/draft/2020-12/schema"
|
||||
| "http://json-schema.org/draft-07/schema#"
|
||||
| "http://json-schema.org/draft-04/schema#";
|
||||
$id?: string;
|
||||
$anchor?: string;
|
||||
$ref?: string;
|
||||
$dynamicRef?: string;
|
||||
$dynamicAnchor?: string;
|
||||
$vocabulary?: Record<string, boolean>;
|
||||
$comment?: string;
|
||||
$defs?: Record<string, JSONSchema>;
|
||||
type?: "object" | "array" | "string" | "number" | "boolean" | "null" | "integer";
|
||||
additionalItems?: _JSONSchema;
|
||||
unevaluatedItems?: _JSONSchema;
|
||||
prefixItems?: _JSONSchema[];
|
||||
items?: _JSONSchema | _JSONSchema[];
|
||||
contains?: _JSONSchema;
|
||||
additionalProperties?: _JSONSchema;
|
||||
unevaluatedProperties?: _JSONSchema;
|
||||
properties?: Record<string, _JSONSchema>;
|
||||
patternProperties?: Record<string, _JSONSchema>;
|
||||
dependentSchemas?: Record<string, _JSONSchema>;
|
||||
propertyNames?: _JSONSchema;
|
||||
if?: _JSONSchema;
|
||||
then?: _JSONSchema;
|
||||
else?: _JSONSchema;
|
||||
allOf?: JSONSchema[];
|
||||
anyOf?: JSONSchema[];
|
||||
oneOf?: JSONSchema[];
|
||||
not?: _JSONSchema;
|
||||
multipleOf?: number;
|
||||
maximum?: number;
|
||||
exclusiveMaximum?: number | boolean;
|
||||
minimum?: number;
|
||||
exclusiveMinimum?: number | boolean;
|
||||
maxLength?: number;
|
||||
minLength?: number;
|
||||
pattern?: string;
|
||||
maxItems?: number;
|
||||
minItems?: number;
|
||||
uniqueItems?: boolean;
|
||||
maxContains?: number;
|
||||
minContains?: number;
|
||||
maxProperties?: number;
|
||||
minProperties?: number;
|
||||
required?: string[];
|
||||
dependentRequired?: Record<string, string[]>;
|
||||
enum?: Array<string | number | boolean | null>;
|
||||
const?: string | number | boolean | null;
|
||||
|
||||
// metadata
|
||||
id?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
default?: unknown;
|
||||
deprecated?: boolean;
|
||||
readOnly?: boolean;
|
||||
writeOnly?: boolean;
|
||||
nullable?: boolean;
|
||||
examples?: unknown[];
|
||||
format?: string;
|
||||
contentMediaType?: string;
|
||||
contentEncoding?: string;
|
||||
contentSchema?: JSONSchema;
|
||||
|
||||
// internal
|
||||
_prefault?: unknown;
|
||||
};
|
||||
|
||||
// for backwards compatibility
|
||||
export type BaseSchema = JSONSchema;
|
||||
|
||||
export interface ObjectSchema extends JSONSchema {
|
||||
type: "object";
|
||||
}
|
||||
|
||||
export interface ArraySchema extends JSONSchema {
|
||||
type: "array";
|
||||
}
|
||||
|
||||
export interface StringSchema extends JSONSchema {
|
||||
type: "string";
|
||||
}
|
||||
|
||||
export interface NumberSchema extends JSONSchema {
|
||||
type: "number";
|
||||
}
|
||||
|
||||
export interface IntegerSchema extends JSONSchema {
|
||||
type: "integer";
|
||||
}
|
||||
|
||||
export interface BooleanSchema extends JSONSchema {
|
||||
type: "boolean";
|
||||
}
|
||||
|
||||
export interface NullSchema extends JSONSchema {
|
||||
type: "null";
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
const locatePath = require('locate-path');
|
||||
const pathExists = require('path-exists');
|
||||
|
||||
const stop = Symbol('findUp.stop');
|
||||
|
||||
module.exports = async (name, options = {}) => {
|
||||
let directory = path.resolve(options.cwd || '');
|
||||
const {root} = path.parse(directory);
|
||||
const paths = [].concat(name);
|
||||
|
||||
const runMatcher = async locateOptions => {
|
||||
if (typeof name !== 'function') {
|
||||
return locatePath(paths, locateOptions);
|
||||
}
|
||||
|
||||
const foundPath = await name(locateOptions.cwd);
|
||||
if (typeof foundPath === 'string') {
|
||||
return locatePath([foundPath], locateOptions);
|
||||
}
|
||||
|
||||
return foundPath;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const foundPath = await runMatcher({...options, cwd: directory});
|
||||
|
||||
if (foundPath === stop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (foundPath) {
|
||||
return path.resolve(directory, foundPath);
|
||||
}
|
||||
|
||||
if (directory === root) {
|
||||
return;
|
||||
}
|
||||
|
||||
directory = path.dirname(directory);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.sync = (name, options = {}) => {
|
||||
let directory = path.resolve(options.cwd || '');
|
||||
const {root} = path.parse(directory);
|
||||
const paths = [].concat(name);
|
||||
|
||||
const runMatcher = locateOptions => {
|
||||
if (typeof name !== 'function') {
|
||||
return locatePath.sync(paths, locateOptions);
|
||||
}
|
||||
|
||||
const foundPath = name(locateOptions.cwd);
|
||||
if (typeof foundPath === 'string') {
|
||||
return locatePath.sync([foundPath], locateOptions);
|
||||
}
|
||||
|
||||
return foundPath;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const foundPath = runMatcher({...options, cwd: directory});
|
||||
|
||||
if (foundPath === stop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (foundPath) {
|
||||
return path.resolve(directory, foundPath);
|
||||
}
|
||||
|
||||
if (directory === root) {
|
||||
return;
|
||||
}
|
||||
|
||||
directory = path.dirname(directory);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.exists = pathExists;
|
||||
|
||||
module.exports.sync.exists = pathExists.sync;
|
||||
|
||||
module.exports.stop = stop;
|
||||
@@ -0,0 +1,805 @@
|
||||
/*
|
||||
Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com>
|
||||
Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*jslint vars:false, bitwise:true*/
|
||||
/*jshint indent:4*/
|
||||
/*global exports:true*/
|
||||
(function clone(exports) {
|
||||
'use strict';
|
||||
|
||||
var Syntax,
|
||||
VisitorOption,
|
||||
VisitorKeys,
|
||||
BREAK,
|
||||
SKIP,
|
||||
REMOVE;
|
||||
|
||||
function deepCopy(obj) {
|
||||
var ret = {}, key, val;
|
||||
for (key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
val = obj[key];
|
||||
if (typeof val === 'object' && val !== null) {
|
||||
ret[key] = deepCopy(val);
|
||||
} else {
|
||||
ret[key] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// based on LLVM libc++ upper_bound / lower_bound
|
||||
// MIT License
|
||||
|
||||
function upperBound(array, func) {
|
||||
var diff, len, i, current;
|
||||
|
||||
len = array.length;
|
||||
i = 0;
|
||||
|
||||
while (len) {
|
||||
diff = len >>> 1;
|
||||
current = i + diff;
|
||||
if (func(array[current])) {
|
||||
len = diff;
|
||||
} else {
|
||||
i = current + 1;
|
||||
len -= diff + 1;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
Syntax = {
|
||||
AssignmentExpression: 'AssignmentExpression',
|
||||
AssignmentPattern: 'AssignmentPattern',
|
||||
ArrayExpression: 'ArrayExpression',
|
||||
ArrayPattern: 'ArrayPattern',
|
||||
ArrowFunctionExpression: 'ArrowFunctionExpression',
|
||||
AwaitExpression: 'AwaitExpression', // CAUTION: It's deferred to ES7.
|
||||
BlockStatement: 'BlockStatement',
|
||||
BinaryExpression: 'BinaryExpression',
|
||||
BreakStatement: 'BreakStatement',
|
||||
CallExpression: 'CallExpression',
|
||||
CatchClause: 'CatchClause',
|
||||
ChainExpression: 'ChainExpression',
|
||||
ClassBody: 'ClassBody',
|
||||
ClassDeclaration: 'ClassDeclaration',
|
||||
ClassExpression: 'ClassExpression',
|
||||
ComprehensionBlock: 'ComprehensionBlock', // CAUTION: It's deferred to ES7.
|
||||
ComprehensionExpression: 'ComprehensionExpression', // CAUTION: It's deferred to ES7.
|
||||
ConditionalExpression: 'ConditionalExpression',
|
||||
ContinueStatement: 'ContinueStatement',
|
||||
DebuggerStatement: 'DebuggerStatement',
|
||||
DirectiveStatement: 'DirectiveStatement',
|
||||
DoWhileStatement: 'DoWhileStatement',
|
||||
EmptyStatement: 'EmptyStatement',
|
||||
ExportAllDeclaration: 'ExportAllDeclaration',
|
||||
ExportDefaultDeclaration: 'ExportDefaultDeclaration',
|
||||
ExportNamedDeclaration: 'ExportNamedDeclaration',
|
||||
ExportSpecifier: 'ExportSpecifier',
|
||||
ExpressionStatement: 'ExpressionStatement',
|
||||
ForStatement: 'ForStatement',
|
||||
ForInStatement: 'ForInStatement',
|
||||
ForOfStatement: 'ForOfStatement',
|
||||
FunctionDeclaration: 'FunctionDeclaration',
|
||||
FunctionExpression: 'FunctionExpression',
|
||||
GeneratorExpression: 'GeneratorExpression', // CAUTION: It's deferred to ES7.
|
||||
Identifier: 'Identifier',
|
||||
IfStatement: 'IfStatement',
|
||||
ImportExpression: 'ImportExpression',
|
||||
ImportDeclaration: 'ImportDeclaration',
|
||||
ImportDefaultSpecifier: 'ImportDefaultSpecifier',
|
||||
ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',
|
||||
ImportSpecifier: 'ImportSpecifier',
|
||||
Literal: 'Literal',
|
||||
LabeledStatement: 'LabeledStatement',
|
||||
LogicalExpression: 'LogicalExpression',
|
||||
MemberExpression: 'MemberExpression',
|
||||
MetaProperty: 'MetaProperty',
|
||||
MethodDefinition: 'MethodDefinition',
|
||||
ModuleSpecifier: 'ModuleSpecifier',
|
||||
NewExpression: 'NewExpression',
|
||||
ObjectExpression: 'ObjectExpression',
|
||||
ObjectPattern: 'ObjectPattern',
|
||||
PrivateIdentifier: 'PrivateIdentifier',
|
||||
Program: 'Program',
|
||||
Property: 'Property',
|
||||
PropertyDefinition: 'PropertyDefinition',
|
||||
RestElement: 'RestElement',
|
||||
ReturnStatement: 'ReturnStatement',
|
||||
SequenceExpression: 'SequenceExpression',
|
||||
SpreadElement: 'SpreadElement',
|
||||
Super: 'Super',
|
||||
SwitchStatement: 'SwitchStatement',
|
||||
SwitchCase: 'SwitchCase',
|
||||
TaggedTemplateExpression: 'TaggedTemplateExpression',
|
||||
TemplateElement: 'TemplateElement',
|
||||
TemplateLiteral: 'TemplateLiteral',
|
||||
ThisExpression: 'ThisExpression',
|
||||
ThrowStatement: 'ThrowStatement',
|
||||
TryStatement: 'TryStatement',
|
||||
UnaryExpression: 'UnaryExpression',
|
||||
UpdateExpression: 'UpdateExpression',
|
||||
VariableDeclaration: 'VariableDeclaration',
|
||||
VariableDeclarator: 'VariableDeclarator',
|
||||
WhileStatement: 'WhileStatement',
|
||||
WithStatement: 'WithStatement',
|
||||
YieldExpression: 'YieldExpression'
|
||||
};
|
||||
|
||||
VisitorKeys = {
|
||||
AssignmentExpression: ['left', 'right'],
|
||||
AssignmentPattern: ['left', 'right'],
|
||||
ArrayExpression: ['elements'],
|
||||
ArrayPattern: ['elements'],
|
||||
ArrowFunctionExpression: ['params', 'body'],
|
||||
AwaitExpression: ['argument'], // CAUTION: It's deferred to ES7.
|
||||
BlockStatement: ['body'],
|
||||
BinaryExpression: ['left', 'right'],
|
||||
BreakStatement: ['label'],
|
||||
CallExpression: ['callee', 'arguments'],
|
||||
CatchClause: ['param', 'body'],
|
||||
ChainExpression: ['expression'],
|
||||
ClassBody: ['body'],
|
||||
ClassDeclaration: ['id', 'superClass', 'body'],
|
||||
ClassExpression: ['id', 'superClass', 'body'],
|
||||
ComprehensionBlock: ['left', 'right'], // CAUTION: It's deferred to ES7.
|
||||
ComprehensionExpression: ['blocks', 'filter', 'body'], // CAUTION: It's deferred to ES7.
|
||||
ConditionalExpression: ['test', 'consequent', 'alternate'],
|
||||
ContinueStatement: ['label'],
|
||||
DebuggerStatement: [],
|
||||
DirectiveStatement: [],
|
||||
DoWhileStatement: ['body', 'test'],
|
||||
EmptyStatement: [],
|
||||
ExportAllDeclaration: ['source'],
|
||||
ExportDefaultDeclaration: ['declaration'],
|
||||
ExportNamedDeclaration: ['declaration', 'specifiers', 'source'],
|
||||
ExportSpecifier: ['exported', 'local'],
|
||||
ExpressionStatement: ['expression'],
|
||||
ForStatement: ['init', 'test', 'update', 'body'],
|
||||
ForInStatement: ['left', 'right', 'body'],
|
||||
ForOfStatement: ['left', 'right', 'body'],
|
||||
FunctionDeclaration: ['id', 'params', 'body'],
|
||||
FunctionExpression: ['id', 'params', 'body'],
|
||||
GeneratorExpression: ['blocks', 'filter', 'body'], // CAUTION: It's deferred to ES7.
|
||||
Identifier: [],
|
||||
IfStatement: ['test', 'consequent', 'alternate'],
|
||||
ImportExpression: ['source'],
|
||||
ImportDeclaration: ['specifiers', 'source'],
|
||||
ImportDefaultSpecifier: ['local'],
|
||||
ImportNamespaceSpecifier: ['local'],
|
||||
ImportSpecifier: ['imported', 'local'],
|
||||
Literal: [],
|
||||
LabeledStatement: ['label', 'body'],
|
||||
LogicalExpression: ['left', 'right'],
|
||||
MemberExpression: ['object', 'property'],
|
||||
MetaProperty: ['meta', 'property'],
|
||||
MethodDefinition: ['key', 'value'],
|
||||
ModuleSpecifier: [],
|
||||
NewExpression: ['callee', 'arguments'],
|
||||
ObjectExpression: ['properties'],
|
||||
ObjectPattern: ['properties'],
|
||||
PrivateIdentifier: [],
|
||||
Program: ['body'],
|
||||
Property: ['key', 'value'],
|
||||
PropertyDefinition: ['key', 'value'],
|
||||
RestElement: [ 'argument' ],
|
||||
ReturnStatement: ['argument'],
|
||||
SequenceExpression: ['expressions'],
|
||||
SpreadElement: ['argument'],
|
||||
Super: [],
|
||||
SwitchStatement: ['discriminant', 'cases'],
|
||||
SwitchCase: ['test', 'consequent'],
|
||||
TaggedTemplateExpression: ['tag', 'quasi'],
|
||||
TemplateElement: [],
|
||||
TemplateLiteral: ['quasis', 'expressions'],
|
||||
ThisExpression: [],
|
||||
ThrowStatement: ['argument'],
|
||||
TryStatement: ['block', 'handler', 'finalizer'],
|
||||
UnaryExpression: ['argument'],
|
||||
UpdateExpression: ['argument'],
|
||||
VariableDeclaration: ['declarations'],
|
||||
VariableDeclarator: ['id', 'init'],
|
||||
WhileStatement: ['test', 'body'],
|
||||
WithStatement: ['object', 'body'],
|
||||
YieldExpression: ['argument']
|
||||
};
|
||||
|
||||
// unique id
|
||||
BREAK = {};
|
||||
SKIP = {};
|
||||
REMOVE = {};
|
||||
|
||||
VisitorOption = {
|
||||
Break: BREAK,
|
||||
Skip: SKIP,
|
||||
Remove: REMOVE
|
||||
};
|
||||
|
||||
function Reference(parent, key) {
|
||||
this.parent = parent;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
Reference.prototype.replace = function replace(node) {
|
||||
this.parent[this.key] = node;
|
||||
};
|
||||
|
||||
Reference.prototype.remove = function remove() {
|
||||
if (Array.isArray(this.parent)) {
|
||||
this.parent.splice(this.key, 1);
|
||||
return true;
|
||||
} else {
|
||||
this.replace(null);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
function Element(node, path, wrap, ref) {
|
||||
this.node = node;
|
||||
this.path = path;
|
||||
this.wrap = wrap;
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
function Controller() { }
|
||||
|
||||
// API:
|
||||
// return property path array from root to current node
|
||||
Controller.prototype.path = function path() {
|
||||
var i, iz, j, jz, result, element;
|
||||
|
||||
function addToPath(result, path) {
|
||||
if (Array.isArray(path)) {
|
||||
for (j = 0, jz = path.length; j < jz; ++j) {
|
||||
result.push(path[j]);
|
||||
}
|
||||
} else {
|
||||
result.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
// root node
|
||||
if (!this.__current.path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// first node is sentinel, second node is root element
|
||||
result = [];
|
||||
for (i = 2, iz = this.__leavelist.length; i < iz; ++i) {
|
||||
element = this.__leavelist[i];
|
||||
addToPath(result, element.path);
|
||||
}
|
||||
addToPath(result, this.__current.path);
|
||||
return result;
|
||||
};
|
||||
|
||||
// API:
|
||||
// return type of current node
|
||||
Controller.prototype.type = function () {
|
||||
var node = this.current();
|
||||
return node.type || this.__current.wrap;
|
||||
};
|
||||
|
||||
// API:
|
||||
// return array of parent elements
|
||||
Controller.prototype.parents = function parents() {
|
||||
var i, iz, result;
|
||||
|
||||
// first node is sentinel
|
||||
result = [];
|
||||
for (i = 1, iz = this.__leavelist.length; i < iz; ++i) {
|
||||
result.push(this.__leavelist[i].node);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// API:
|
||||
// return current node
|
||||
Controller.prototype.current = function current() {
|
||||
return this.__current.node;
|
||||
};
|
||||
|
||||
Controller.prototype.__execute = function __execute(callback, element) {
|
||||
var previous, result;
|
||||
|
||||
result = undefined;
|
||||
|
||||
previous = this.__current;
|
||||
this.__current = element;
|
||||
this.__state = null;
|
||||
if (callback) {
|
||||
result = callback.call(this, element.node, this.__leavelist[this.__leavelist.length - 1].node);
|
||||
}
|
||||
this.__current = previous;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// API:
|
||||
// notify control skip / break
|
||||
Controller.prototype.notify = function notify(flag) {
|
||||
this.__state = flag;
|
||||
};
|
||||
|
||||
// API:
|
||||
// skip child nodes of current node
|
||||
Controller.prototype.skip = function () {
|
||||
this.notify(SKIP);
|
||||
};
|
||||
|
||||
// API:
|
||||
// break traversals
|
||||
Controller.prototype['break'] = function () {
|
||||
this.notify(BREAK);
|
||||
};
|
||||
|
||||
// API:
|
||||
// remove node
|
||||
Controller.prototype.remove = function () {
|
||||
this.notify(REMOVE);
|
||||
};
|
||||
|
||||
Controller.prototype.__initialize = function(root, visitor) {
|
||||
this.visitor = visitor;
|
||||
this.root = root;
|
||||
this.__worklist = [];
|
||||
this.__leavelist = [];
|
||||
this.__current = null;
|
||||
this.__state = null;
|
||||
this.__fallback = null;
|
||||
if (visitor.fallback === 'iteration') {
|
||||
this.__fallback = Object.keys;
|
||||
} else if (typeof visitor.fallback === 'function') {
|
||||
this.__fallback = visitor.fallback;
|
||||
}
|
||||
|
||||
this.__keys = VisitorKeys;
|
||||
if (visitor.keys) {
|
||||
this.__keys = Object.assign(Object.create(this.__keys), visitor.keys);
|
||||
}
|
||||
};
|
||||
|
||||
function isNode(node) {
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
return typeof node === 'object' && typeof node.type === 'string';
|
||||
}
|
||||
|
||||
function isProperty(nodeType, key) {
|
||||
return (nodeType === Syntax.ObjectExpression || nodeType === Syntax.ObjectPattern) && 'properties' === key;
|
||||
}
|
||||
|
||||
function candidateExistsInLeaveList(leavelist, candidate) {
|
||||
for (var i = leavelist.length - 1; i >= 0; --i) {
|
||||
if (leavelist[i].node === candidate) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Controller.prototype.traverse = function traverse(root, visitor) {
|
||||
var worklist,
|
||||
leavelist,
|
||||
element,
|
||||
node,
|
||||
nodeType,
|
||||
ret,
|
||||
key,
|
||||
current,
|
||||
current2,
|
||||
candidates,
|
||||
candidate,
|
||||
sentinel;
|
||||
|
||||
this.__initialize(root, visitor);
|
||||
|
||||
sentinel = {};
|
||||
|
||||
// reference
|
||||
worklist = this.__worklist;
|
||||
leavelist = this.__leavelist;
|
||||
|
||||
// initialize
|
||||
worklist.push(new Element(root, null, null, null));
|
||||
leavelist.push(new Element(null, null, null, null));
|
||||
|
||||
while (worklist.length) {
|
||||
element = worklist.pop();
|
||||
|
||||
if (element === sentinel) {
|
||||
element = leavelist.pop();
|
||||
|
||||
ret = this.__execute(visitor.leave, element);
|
||||
|
||||
if (this.__state === BREAK || ret === BREAK) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (element.node) {
|
||||
|
||||
ret = this.__execute(visitor.enter, element);
|
||||
|
||||
if (this.__state === BREAK || ret === BREAK) {
|
||||
return;
|
||||
}
|
||||
|
||||
worklist.push(sentinel);
|
||||
leavelist.push(element);
|
||||
|
||||
if (this.__state === SKIP || ret === SKIP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
node = element.node;
|
||||
nodeType = node.type || element.wrap;
|
||||
candidates = this.__keys[nodeType];
|
||||
if (!candidates) {
|
||||
if (this.__fallback) {
|
||||
candidates = this.__fallback(node);
|
||||
} else {
|
||||
throw new Error('Unknown node type ' + nodeType + '.');
|
||||
}
|
||||
}
|
||||
|
||||
current = candidates.length;
|
||||
while ((current -= 1) >= 0) {
|
||||
key = candidates[current];
|
||||
candidate = node[key];
|
||||
if (!candidate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Array.isArray(candidate)) {
|
||||
current2 = candidate.length;
|
||||
while ((current2 -= 1) >= 0) {
|
||||
if (!candidate[current2]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (candidateExistsInLeaveList(leavelist, candidate[current2])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isProperty(nodeType, candidates[current])) {
|
||||
element = new Element(candidate[current2], [key, current2], 'Property', null);
|
||||
} else if (isNode(candidate[current2])) {
|
||||
element = new Element(candidate[current2], [key, current2], null, null);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
worklist.push(element);
|
||||
}
|
||||
} else if (isNode(candidate)) {
|
||||
if (candidateExistsInLeaveList(leavelist, candidate)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
worklist.push(new Element(candidate, key, null, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Controller.prototype.replace = function replace(root, visitor) {
|
||||
var worklist,
|
||||
leavelist,
|
||||
node,
|
||||
nodeType,
|
||||
target,
|
||||
element,
|
||||
current,
|
||||
current2,
|
||||
candidates,
|
||||
candidate,
|
||||
sentinel,
|
||||
outer,
|
||||
key;
|
||||
|
||||
function removeElem(element) {
|
||||
var i,
|
||||
key,
|
||||
nextElem,
|
||||
parent;
|
||||
|
||||
if (element.ref.remove()) {
|
||||
// When the reference is an element of an array.
|
||||
key = element.ref.key;
|
||||
parent = element.ref.parent;
|
||||
|
||||
// If removed from array, then decrease following items' keys.
|
||||
i = worklist.length;
|
||||
while (i--) {
|
||||
nextElem = worklist[i];
|
||||
if (nextElem.ref && nextElem.ref.parent === parent) {
|
||||
if (nextElem.ref.key < key) {
|
||||
break;
|
||||
}
|
||||
--nextElem.ref.key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.__initialize(root, visitor);
|
||||
|
||||
sentinel = {};
|
||||
|
||||
// reference
|
||||
worklist = this.__worklist;
|
||||
leavelist = this.__leavelist;
|
||||
|
||||
// initialize
|
||||
outer = {
|
||||
root: root
|
||||
};
|
||||
element = new Element(root, null, null, new Reference(outer, 'root'));
|
||||
worklist.push(element);
|
||||
leavelist.push(element);
|
||||
|
||||
while (worklist.length) {
|
||||
element = worklist.pop();
|
||||
|
||||
if (element === sentinel) {
|
||||
element = leavelist.pop();
|
||||
|
||||
target = this.__execute(visitor.leave, element);
|
||||
|
||||
// node may be replaced with null,
|
||||
// so distinguish between undefined and null in this place
|
||||
if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) {
|
||||
// replace
|
||||
element.ref.replace(target);
|
||||
}
|
||||
|
||||
if (this.__state === REMOVE || target === REMOVE) {
|
||||
removeElem(element);
|
||||
}
|
||||
|
||||
if (this.__state === BREAK || target === BREAK) {
|
||||
return outer.root;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
target = this.__execute(visitor.enter, element);
|
||||
|
||||
// node may be replaced with null,
|
||||
// so distinguish between undefined and null in this place
|
||||
if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) {
|
||||
// replace
|
||||
element.ref.replace(target);
|
||||
element.node = target;
|
||||
}
|
||||
|
||||
if (this.__state === REMOVE || target === REMOVE) {
|
||||
removeElem(element);
|
||||
element.node = null;
|
||||
}
|
||||
|
||||
if (this.__state === BREAK || target === BREAK) {
|
||||
return outer.root;
|
||||
}
|
||||
|
||||
// node may be null
|
||||
node = element.node;
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
worklist.push(sentinel);
|
||||
leavelist.push(element);
|
||||
|
||||
if (this.__state === SKIP || target === SKIP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nodeType = node.type || element.wrap;
|
||||
candidates = this.__keys[nodeType];
|
||||
if (!candidates) {
|
||||
if (this.__fallback) {
|
||||
candidates = this.__fallback(node);
|
||||
} else {
|
||||
throw new Error('Unknown node type ' + nodeType + '.');
|
||||
}
|
||||
}
|
||||
|
||||
current = candidates.length;
|
||||
while ((current -= 1) >= 0) {
|
||||
key = candidates[current];
|
||||
candidate = node[key];
|
||||
if (!candidate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Array.isArray(candidate)) {
|
||||
current2 = candidate.length;
|
||||
while ((current2 -= 1) >= 0) {
|
||||
if (!candidate[current2]) {
|
||||
continue;
|
||||
}
|
||||
if (isProperty(nodeType, candidates[current])) {
|
||||
element = new Element(candidate[current2], [key, current2], 'Property', new Reference(candidate, current2));
|
||||
} else if (isNode(candidate[current2])) {
|
||||
element = new Element(candidate[current2], [key, current2], null, new Reference(candidate, current2));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
worklist.push(element);
|
||||
}
|
||||
} else if (isNode(candidate)) {
|
||||
worklist.push(new Element(candidate, key, null, new Reference(node, key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outer.root;
|
||||
};
|
||||
|
||||
function traverse(root, visitor) {
|
||||
var controller = new Controller();
|
||||
return controller.traverse(root, visitor);
|
||||
}
|
||||
|
||||
function replace(root, visitor) {
|
||||
var controller = new Controller();
|
||||
return controller.replace(root, visitor);
|
||||
}
|
||||
|
||||
function extendCommentRange(comment, tokens) {
|
||||
var target;
|
||||
|
||||
target = upperBound(tokens, function search(token) {
|
||||
return token.range[0] > comment.range[0];
|
||||
});
|
||||
|
||||
comment.extendedRange = [comment.range[0], comment.range[1]];
|
||||
|
||||
if (target !== tokens.length) {
|
||||
comment.extendedRange[1] = tokens[target].range[0];
|
||||
}
|
||||
|
||||
target -= 1;
|
||||
if (target >= 0) {
|
||||
comment.extendedRange[0] = tokens[target].range[1];
|
||||
}
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
function attachComments(tree, providedComments, tokens) {
|
||||
// At first, we should calculate extended comment ranges.
|
||||
var comments = [], comment, len, i, cursor;
|
||||
|
||||
if (!tree.range) {
|
||||
throw new Error('attachComments needs range information');
|
||||
}
|
||||
|
||||
// tokens array is empty, we attach comments to tree as 'leadingComments'
|
||||
if (!tokens.length) {
|
||||
if (providedComments.length) {
|
||||
for (i = 0, len = providedComments.length; i < len; i += 1) {
|
||||
comment = deepCopy(providedComments[i]);
|
||||
comment.extendedRange = [0, tree.range[0]];
|
||||
comments.push(comment);
|
||||
}
|
||||
tree.leadingComments = comments;
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
for (i = 0, len = providedComments.length; i < len; i += 1) {
|
||||
comments.push(extendCommentRange(deepCopy(providedComments[i]), tokens));
|
||||
}
|
||||
|
||||
// This is based on John Freeman's implementation.
|
||||
cursor = 0;
|
||||
traverse(tree, {
|
||||
enter: function (node) {
|
||||
var comment;
|
||||
|
||||
while (cursor < comments.length) {
|
||||
comment = comments[cursor];
|
||||
if (comment.extendedRange[1] > node.range[0]) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (comment.extendedRange[1] === node.range[0]) {
|
||||
if (!node.leadingComments) {
|
||||
node.leadingComments = [];
|
||||
}
|
||||
node.leadingComments.push(comment);
|
||||
comments.splice(cursor, 1);
|
||||
} else {
|
||||
cursor += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// already out of owned node
|
||||
if (cursor === comments.length) {
|
||||
return VisitorOption.Break;
|
||||
}
|
||||
|
||||
if (comments[cursor].extendedRange[0] > node.range[1]) {
|
||||
return VisitorOption.Skip;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cursor = 0;
|
||||
traverse(tree, {
|
||||
leave: function (node) {
|
||||
var comment;
|
||||
|
||||
while (cursor < comments.length) {
|
||||
comment = comments[cursor];
|
||||
if (node.range[1] < comment.extendedRange[0]) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (node.range[1] === comment.extendedRange[0]) {
|
||||
if (!node.trailingComments) {
|
||||
node.trailingComments = [];
|
||||
}
|
||||
node.trailingComments.push(comment);
|
||||
comments.splice(cursor, 1);
|
||||
} else {
|
||||
cursor += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// already out of owned node
|
||||
if (cursor === comments.length) {
|
||||
return VisitorOption.Break;
|
||||
}
|
||||
|
||||
if (comments[cursor].extendedRange[0] > node.range[1]) {
|
||||
return VisitorOption.Skip;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
exports.Syntax = Syntax;
|
||||
exports.traverse = traverse;
|
||||
exports.replace = replace;
|
||||
exports.attachComments = attachComments;
|
||||
exports.VisitorKeys = VisitorKeys;
|
||||
exports.VisitorOption = VisitorOption;
|
||||
exports.Controller = Controller;
|
||||
exports.cloneEnvironment = function () { return clone({}); };
|
||||
|
||||
return exports;
|
||||
}(exports));
|
||||
/* vim: set sw=4 ts=4 et tw=80 : */
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021 Paolo Insogna and the real-require contributors
|
||||
|
||||
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,6 @@
|
||||
function _class_check_private_static_field_descriptor(descriptor, action) {
|
||||
if (descriptor === undefined) {
|
||||
throw new TypeError("attempted to " + action + " private static field before its declaration");
|
||||
}
|
||||
}
|
||||
export { _class_check_private_static_field_descriptor as _ };
|
||||
Reference in New Issue
Block a user