Publish
This commit is contained in:
25
task/benchmark/compression/index.ts
Normal file
25
task/benchmark/compression/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { shell } from '@sinclair/hammer'
|
||||
import { statSync, readdirSync } from 'fs'
|
||||
import { basename, extname } from 'path'
|
||||
|
||||
export async function measure(test: string) {
|
||||
await shell(`hammer build task/benchmark/compression/module/${test}.ts --dist target/benchmark/compression`)
|
||||
const compiled = statSync(`target/benchmark/compression/${test}.js`)
|
||||
await shell(`hammer build task/benchmark/compression/module/${test}.ts --dist target/benchmark/compression --minify`)
|
||||
const minified = statSync(`target/benchmark/compression/${test}.js`)
|
||||
return {
|
||||
test: test.padEnd(20),
|
||||
compiled: `${(compiled.size / 1000).toFixed(1)} kb`.padStart(8),
|
||||
minified: `${(minified.size / 1000).toFixed(1)} kb`.padStart(8),
|
||||
ratio: compiled.size / minified.size,
|
||||
}
|
||||
}
|
||||
|
||||
export async function compression() {
|
||||
const tests = readdirSync('task/benchmark/compression/module').map((name) => basename(name, extname(name)))
|
||||
const results = await Promise.all(tests.map((test) => measure(test)))
|
||||
const present = results.reduce((acc, c) => {
|
||||
return { ...acc, [c.test.replace(/-/g, '/')]: { Compiled: c.compiled, Minified: c.minified, Compression: `${c.ratio.toFixed(2)} x` } }
|
||||
}, {})
|
||||
console.table(present)
|
||||
}
|
||||
3
task/benchmark/compression/module/typebox-compiler.ts
Normal file
3
task/benchmark/compression/module/typebox-compiler.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { TypeCompiler } from '@sinclair/typebox/compiler'
|
||||
|
||||
console.log(TypeCompiler)
|
||||
3
task/benchmark/compression/module/typebox-errors.ts
Normal file
3
task/benchmark/compression/module/typebox-errors.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import * as Errors from '@sinclair/typebox/errors'
|
||||
|
||||
console.log(Errors)
|
||||
3
task/benchmark/compression/module/typebox-syntax.ts
Normal file
3
task/benchmark/compression/module/typebox-syntax.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import * as Syntax from '@sinclair/typebox/syntax'
|
||||
|
||||
console.log(Syntax)
|
||||
3
task/benchmark/compression/module/typebox-system.ts
Normal file
3
task/benchmark/compression/module/typebox-system.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { TypeSystem } from '@sinclair/typebox/system'
|
||||
|
||||
console.log(TypeSystem)
|
||||
3
task/benchmark/compression/module/typebox-value.ts
Normal file
3
task/benchmark/compression/module/typebox-value.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
|
||||
console.log(Value)
|
||||
3
task/benchmark/compression/module/typebox.ts
Normal file
3
task/benchmark/compression/module/typebox.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
|
||||
const T = Type.String()
|
||||
2
task/benchmark/index.ts
Normal file
2
task/benchmark/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './compression/index'
|
||||
export * from './measurement/index'
|
||||
5
task/benchmark/measurement/index.ts
Normal file
5
task/benchmark/measurement/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { shell } from '@sinclair/hammer'
|
||||
|
||||
export async function measurement() {
|
||||
await shell(`hammer run task/benchmark/measurement/module/index.ts --dist target/benchmark/measurement`)
|
||||
}
|
||||
7
task/benchmark/measurement/module/benchmark.ts
Normal file
7
task/benchmark/measurement/module/benchmark.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export namespace Benchmark {
|
||||
export function Measure(execute: Function, iterations: number = 16_000_000) {
|
||||
const start = Date.now()
|
||||
for (let i = 0; i < iterations; i++) execute()
|
||||
return { iterations, completed: Date.now() - start }
|
||||
}
|
||||
}
|
||||
146
task/benchmark/measurement/module/cases.ts
Normal file
146
task/benchmark/measurement/module/cases.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
|
||||
export namespace Cases {
|
||||
export const Literal_String = Type.Literal('hello')
|
||||
|
||||
export const Literal_Number = Type.Literal(1)
|
||||
|
||||
export const Literal_Boolean = Type.Literal(true)
|
||||
|
||||
export const Primitive_Number = Type.Number()
|
||||
|
||||
export const Primitive_String = Type.String()
|
||||
|
||||
export const Primitive_String_Pattern = Type.String({ pattern: 'foo', default: 'foo' })
|
||||
|
||||
export const Primitive_Boolean = Type.Boolean()
|
||||
|
||||
export const Primitive_Null = Type.Null()
|
||||
|
||||
export const Object_Unconstrained = Type.Object({
|
||||
number: Type.Number(),
|
||||
negNumber: Type.Number(),
|
||||
maxNumber: Type.Number(),
|
||||
string: Type.String(),
|
||||
longString: Type.String(),
|
||||
boolean: Type.Boolean(),
|
||||
deeplyNested: Type.Object({
|
||||
foo: Type.String(),
|
||||
num: Type.Number(),
|
||||
bool: Type.Boolean(),
|
||||
}),
|
||||
})
|
||||
|
||||
export const Object_Constrained = Type.Object(Object_Unconstrained.properties, {
|
||||
additionalProperties: false,
|
||||
})
|
||||
|
||||
export const Object_Vector3 = Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
})
|
||||
|
||||
export const Object_Box3D = Type.Object({
|
||||
scale: Object_Vector3,
|
||||
position: Object_Vector3,
|
||||
rotate: Object_Vector3,
|
||||
pivot: Object_Vector3,
|
||||
})
|
||||
export const Object_Recursive = Type.Recursive(
|
||||
(Recursive) =>
|
||||
Type.Object({
|
||||
id: Type.String(),
|
||||
nodes: Type.Array(Recursive),
|
||||
}),
|
||||
{
|
||||
default: {
|
||||
id: '',
|
||||
nodes: [
|
||||
{
|
||||
id: '',
|
||||
nodes: [
|
||||
{ id: '', nodes: [] },
|
||||
{ id: '', nodes: [] },
|
||||
{ id: '', nodes: [] },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
nodes: [
|
||||
{ id: '', nodes: [] },
|
||||
{ id: '', nodes: [] },
|
||||
{ id: '', nodes: [] },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
nodes: [
|
||||
{ id: '', nodes: [] },
|
||||
{ id: '', nodes: [] },
|
||||
{ id: '', nodes: [] },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// prettier-ignore
|
||||
export const Tuple_Primitive = Type.Tuple([
|
||||
Type.String(),
|
||||
Type.Number(),
|
||||
Type.Boolean()
|
||||
])
|
||||
// prettier-ignore
|
||||
export const Tuple_Object = Type.Tuple([
|
||||
Type.Object({ x: Type.Number(), y: Type.Number() }),
|
||||
Type.Object({ a: Type.String(), b: Type.String() })
|
||||
])
|
||||
// prettier-ignore
|
||||
export const Composite_Intersect = Type.Intersect([
|
||||
Type.Object({ x: Type.Number(), y: Type.Number() }),
|
||||
Type.Object({ a: Type.String(), b: Type.String() })
|
||||
], { default: { x: 1, y: 2, a: 'a', b: 'b' } })
|
||||
|
||||
// prettier-ignore
|
||||
export const Composite_Union = Type.Union([
|
||||
Type.Object({ x: Type.Number(), y: Type.Number() }),
|
||||
Type.Object({ a: Type.String(), b: Type.String() })
|
||||
], { default: { a: 'a', b: 'b' } })
|
||||
|
||||
export const Math_Vector4 = Type.Tuple([Type.Number(), Type.Number(), Type.Number(), Type.Number()])
|
||||
|
||||
export const Math_Matrix4 = Type.Array(Type.Array(Type.Number()), {
|
||||
default: [
|
||||
[1, 0, 0, 0],
|
||||
[0, 1, 0, 0],
|
||||
[0, 0, 1, 0],
|
||||
[0, 0, 0, 1],
|
||||
],
|
||||
})
|
||||
|
||||
export const Array_Primitive_Number = Type.Array(Type.Number(), { minItems: 4 })
|
||||
|
||||
export const Array_Primitive_String = Type.Array(Type.String(), { minItems: 4 })
|
||||
|
||||
export const Array_Primitive_Boolean = Type.Array(Type.Boolean(), { minItems: 4 })
|
||||
|
||||
export const Array_Object_Unconstrained = Type.Array(Object_Unconstrained, { minItems: 4 })
|
||||
|
||||
export const Array_Object_Constrained = Type.Array(Object_Constrained, { minItems: 4 })
|
||||
|
||||
export const Array_Object_Recursive = Type.Array(Object_Recursive, { minItems: 4 })
|
||||
|
||||
export const Array_Tuple_Primitive = Type.Array(Tuple_Primitive, { minItems: 4 })
|
||||
|
||||
export const Array_Tuple_Object = Type.Array(Tuple_Object, { minItems: 4 })
|
||||
|
||||
export const Array_Composite_Intersect = Type.Array(Composite_Intersect, { minItems: 4 })
|
||||
|
||||
export const Array_Composite_Union = Type.Array(Composite_Union, { minItems: 4 })
|
||||
|
||||
export const Array_Math_Vector4 = Type.Array(Math_Vector4, { minItems: 4 })
|
||||
|
||||
export const Array_Math_Matrix4 = Type.Array(Math_Matrix4, { minItems: 4 })
|
||||
}
|
||||
41
task/benchmark/measurement/module/check.ts
Normal file
41
task/benchmark/measurement/module/check.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Cases } from './cases'
|
||||
import { Benchmark } from './benchmark'
|
||||
import { TypeCompiler } from '@sinclair/typebox/compiler'
|
||||
import { TSchema, TypeGuard } from '@sinclair/typebox'
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
|
||||
import Ajv from 'ajv'
|
||||
|
||||
const ajv = new Ajv() // ensure single instance
|
||||
|
||||
export namespace CheckBenchmark {
|
||||
function Measure<T extends TSchema>(type: string, schema: T) {
|
||||
console.log('CheckBenchmark.Measure(', type, ')')
|
||||
|
||||
const iterations = 1_000_000
|
||||
const V = Value.Create(schema)
|
||||
|
||||
const AC = ajv.compile(schema)
|
||||
const A = Benchmark.Measure(() => {
|
||||
if (!AC(V)) throw Error()
|
||||
}, iterations)
|
||||
|
||||
const CC = TypeCompiler.Compile(schema)
|
||||
const T = Benchmark.Measure(() => {
|
||||
if (!CC.Check(V)) throw Error()
|
||||
}, iterations)
|
||||
|
||||
const VC = Benchmark.Measure(() => {
|
||||
if (!Value.Check(schema, V)) throw Error()
|
||||
}, iterations)
|
||||
|
||||
return { type, ajv: A, compiler: T, value: VC }
|
||||
}
|
||||
|
||||
export function* Execute() {
|
||||
for (const [type, schema] of Object.entries(Cases)) {
|
||||
if (!TypeGuard.IsSchema(schema)) throw Error('Invalid TypeBox schema')
|
||||
yield Measure(type, schema)
|
||||
}
|
||||
}
|
||||
}
|
||||
35
task/benchmark/measurement/module/compile.ts
Normal file
35
task/benchmark/measurement/module/compile.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Cases } from './cases'
|
||||
import { Benchmark } from './benchmark'
|
||||
import { TypeCompiler } from '@sinclair/typebox/compiler'
|
||||
import { TSchema, TypeGuard } from '@sinclair/typebox'
|
||||
import Ajv from 'ajv'
|
||||
|
||||
const ajv = new Ajv() // ensure single instance
|
||||
|
||||
export namespace CompileBenchmark {
|
||||
function Measure<T extends TSchema>(type: string, schema: T) {
|
||||
const iterations = 1000
|
||||
console.log('CompileBenchmark.Measure(', type, ')')
|
||||
// -------------------------------------------------------------------------------
|
||||
// Note: Ajv caches schemas by reference. To ensure we measure actual
|
||||
// compilation times, we must pass a new reference via { ...schema }
|
||||
// -------------------------------------------------------------------------------
|
||||
const AC = Benchmark.Measure(() => ajv.compile({ ...schema }), iterations)
|
||||
const CC = Benchmark.Measure(() => TypeCompiler.Compile({ ...schema }), iterations)
|
||||
return { type, ajv: AC, compiler: CC }
|
||||
}
|
||||
|
||||
export function* Execute() {
|
||||
for (const [type, schema] of Object.entries(Cases)) {
|
||||
if (!TypeGuard.IsSchema(schema)) throw Error('Invalid TypeBox schema')
|
||||
// -------------------------------------------------------------------------------
|
||||
// Note: it is not possible to benchmark recursive schemas as ajv will cache and
|
||||
// track duplicate $id (resulting in compile error). It is not possible to ammend
|
||||
// recursive $id's without potentially biasing results, so we omit on this case.
|
||||
// -------------------------------------------------------------------------------
|
||||
if (type === 'Object_Recursive' || type === 'Array_Object_Recursive') continue
|
||||
|
||||
yield Measure(type, schema)
|
||||
}
|
||||
}
|
||||
}
|
||||
36
task/benchmark/measurement/module/index.ts
Normal file
36
task/benchmark/measurement/module/index.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { CompileBenchmark } from './compile'
|
||||
import { CheckBenchmark } from './check'
|
||||
import { Result } from './result'
|
||||
|
||||
export function present(results: Result[]) {
|
||||
console.table(
|
||||
results.reduce((acc, result) => {
|
||||
const ratio = result.ajv.completed / result.compiler.completed
|
||||
if (result.value) {
|
||||
return {
|
||||
...acc,
|
||||
[result.type.padEnd(26, ' ')]: {
|
||||
Iterations: result.compiler.iterations,
|
||||
ValueCheck: `${result.value.completed} ms`.padStart(10),
|
||||
Ajv: `${result.ajv.completed} ms`.padStart(10),
|
||||
TypeCompiler: `${result.compiler.completed} ms`.padStart(10),
|
||||
Performance: `${ratio.toFixed(2)} x`.padStart(10, ' '),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
...acc,
|
||||
[result.type.padEnd(26, ' ')]: {
|
||||
Iterations: result.compiler.iterations,
|
||||
Ajv: `${result.ajv.completed} ms`.padStart(10),
|
||||
TypeCompiler: `${result.compiler.completed} ms`.padStart(10),
|
||||
Performance: `${ratio.toFixed(2)} x`.padStart(10, ' '),
|
||||
},
|
||||
}
|
||||
}
|
||||
}, {}),
|
||||
)
|
||||
}
|
||||
|
||||
present([...CompileBenchmark.Execute()])
|
||||
present([...CheckBenchmark.Execute()])
|
||||
15
task/benchmark/measurement/module/result.ts
Normal file
15
task/benchmark/measurement/module/result.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export type Result = {
|
||||
type: string
|
||||
ajv: {
|
||||
iterations: number
|
||||
completed: number
|
||||
}
|
||||
compiler: {
|
||||
iterations: number
|
||||
completed: number
|
||||
}
|
||||
value?: {
|
||||
iterations: number
|
||||
completed: number
|
||||
}
|
||||
}
|
||||
38
task/build/cjs/build.ts
Normal file
38
task/build/cjs/build.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import { removeNotices } from '../notices/remove-notices'
|
||||
import { compile } from './compile'
|
||||
|
||||
/** Builds the CommonJS version of this package */
|
||||
export async function build(target: string) {
|
||||
console.log('building...cjs')
|
||||
const buildTarget = `${target}/build/cjs`
|
||||
await compile(buildTarget)
|
||||
await removeNotices(buildTarget)
|
||||
}
|
||||
41
task/build/cjs/compile.ts
Normal file
41
task/build/cjs/compile.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
declare function shell(command: string): Promise<void>
|
||||
|
||||
// prettier-ignore
|
||||
export async function compile(target: string) {
|
||||
const options = [
|
||||
`--outDir ${target}`,
|
||||
'--target ES2020',
|
||||
'--module Node16',
|
||||
'--moduleResolution Node16',
|
||||
'--declaration',
|
||||
].join(' ')
|
||||
await shell(`tsc -p ./src/tsconfig.json ${options}`)
|
||||
}
|
||||
40
task/build/esm/build.ts
Normal file
40
task/build/esm/build.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import { removeNotices } from '../notices/remove-notices'
|
||||
import { convertToEsm } from './convert-to-esm'
|
||||
import { compile } from './compile'
|
||||
|
||||
/** Builds the ESM version of this package */
|
||||
export async function build(target: string) {
|
||||
console.log('building...esm')
|
||||
const buildTarget = `${target}/build/esm`
|
||||
await compile(buildTarget)
|
||||
await convertToEsm(buildTarget)
|
||||
await removeNotices(buildTarget)
|
||||
}
|
||||
41
task/build/esm/compile.ts
Normal file
41
task/build/esm/compile.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
declare function shell(command: string): Promise<void>
|
||||
|
||||
// prettier-ignore
|
||||
export async function compile(target: string) {
|
||||
const options = [
|
||||
`--outDir ${target}`,
|
||||
'--target ES2020',
|
||||
'--module ESNext',
|
||||
'--moduleResolution Bundler',
|
||||
'--declaration',
|
||||
].join(' ')
|
||||
await shell(`tsc -p ./src/tsconfig.json ${options}`)
|
||||
}
|
||||
110
task/build/esm/convert-to-esm.ts
Normal file
110
task/build/esm/convert-to-esm.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import * as Path from 'node:path'
|
||||
import * as Fs from 'node:fs'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Specifier Rewrite
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
// prettier-ignore
|
||||
function replaceInlineImportSpecifiers(content: string): string {
|
||||
const pattern = /import\((.*?)\)/g
|
||||
while (true) {
|
||||
const match = pattern.exec(content)
|
||||
if (match === null) return content
|
||||
const captured = match[1]
|
||||
if(captured.includes('.mjs')) continue
|
||||
const specifier = captured.slice(1, captured.length - 1)
|
||||
content = content.replace(captured, `"${specifier}.mjs"`)
|
||||
}
|
||||
}
|
||||
// prettier-ignore
|
||||
function replaceExportSpecifiers(content: string): string {
|
||||
const pattern = /(export|import)(.*) from ('(.*)');/g
|
||||
while(true) {
|
||||
const match = pattern.exec(content)
|
||||
if(match === null) return content
|
||||
const captured = match[3]
|
||||
const specifier = captured.slice(1, captured.length - 1)
|
||||
content = content.replace(captured, `'${specifier}.mjs'`)
|
||||
}
|
||||
}
|
||||
function replaceSpecifiers(content: string): string {
|
||||
const pass1 = replaceExportSpecifiers(content)
|
||||
const pass2 = replaceInlineImportSpecifiers(pass1)
|
||||
return pass2
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ConvertToEsm
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
function shouldProcess(sourcePath: string) {
|
||||
const extname = Path.extname(sourcePath)
|
||||
return ['.js', '.ts'].includes(extname)
|
||||
}
|
||||
// prettier-ignore
|
||||
function newExtension(extname: string) {
|
||||
return (
|
||||
extname === '.js' ? '.mjs' :
|
||||
extname === '.ts' ? '.mts' :
|
||||
extname
|
||||
)
|
||||
}
|
||||
// prettier-ignore
|
||||
function processFile(sourcePath: string) {
|
||||
if(!shouldProcess(sourcePath)) return
|
||||
const extname = Path.extname(sourcePath)
|
||||
const dirname = Path.dirname(sourcePath)
|
||||
const basename = Path.basename(sourcePath, extname)
|
||||
const new_extname = newExtension(extname)
|
||||
const sourceContent = Fs.readFileSync(sourcePath, 'utf-8')
|
||||
const targetContent = replaceSpecifiers(sourceContent)
|
||||
const targetPath = `${Path.join(dirname, basename)}${new_extname}`
|
||||
Fs.writeFileSync(sourcePath, targetContent)
|
||||
Fs.renameSync(sourcePath, targetPath)
|
||||
}
|
||||
// prettier-ignore
|
||||
function processSourcePath(sourcePath: string) {
|
||||
const stat = Fs.statSync(sourcePath)
|
||||
if(stat.isDirectory()) return readDirectory(sourcePath)
|
||||
if(stat.isFile()) return processFile(sourcePath)
|
||||
}
|
||||
// prettier-ignore
|
||||
function readDirectory(sourceDirectory: string) {
|
||||
for(const entry of Fs.readdirSync(sourceDirectory)) {
|
||||
const sourcePath = Path.join(sourceDirectory, entry)
|
||||
processSourcePath(sourcePath)
|
||||
}
|
||||
}
|
||||
/** Converts the JavaScript and TypeScript declaration modules in the given source directory to use .mjs extensions */
|
||||
export function convertToEsm(sourceDirectory: string) {
|
||||
readDirectory(sourceDirectory)
|
||||
}
|
||||
31
task/build/index.ts
Normal file
31
task/build/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * as Package from './package/build'
|
||||
export * as Esm from './esm/build'
|
||||
export * as Cjs from './cjs/build'
|
||||
82
task/build/notices/remove-notices.ts
Normal file
82
task/build/notices/remove-notices.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import * as Path from 'node:path'
|
||||
import * as Fs from 'node:fs'
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Remove Module Level MIT Notice on Package Distribution
|
||||
//
|
||||
// The MIT copyright notice the unnecessarily increases the distribution
|
||||
// size of the package, this code removes it. The MIT license is available
|
||||
// in the package root.
|
||||
//
|
||||
// ----------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
function escape(content: string) {
|
||||
return content.split('').map((c) => `\\${c}`).join('')
|
||||
}
|
||||
// prettier-ignore
|
||||
function removeNotice(content: string): string {
|
||||
const open = escape('/*--------------------------------------------------------------------------')
|
||||
const close = escape('---------------------------------------------------------------------------*/')
|
||||
const critera = 'Permission is hereby granted, free of charge'
|
||||
const pattern = new RegExp(`${open}[\\s\\S]*?${close}`, 'gm')
|
||||
while (true) {
|
||||
const match = pattern.exec(content)
|
||||
if (match === null) return content.trimStart()
|
||||
if (!match[0].includes(critera)) continue
|
||||
content = content.replace(match[0], '')
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Directory Enumeration
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
function processFile(sourcePath: string) {
|
||||
const sourceContent = Fs.readFileSync(sourcePath, 'utf-8')
|
||||
const targetContent = removeNotice(sourceContent)
|
||||
Fs.writeFileSync(sourcePath, targetContent)
|
||||
}
|
||||
// prettier-ignore
|
||||
function processSourcePath(sourcePath: string) {
|
||||
const stat = Fs.statSync(sourcePath)
|
||||
if(stat.isDirectory()) return readDirectory(sourcePath)
|
||||
if(stat.isFile()) return processFile(sourcePath)
|
||||
}
|
||||
// prettier-ignore
|
||||
function readDirectory(sourceDirectory: string) {
|
||||
for(const entry of Fs.readdirSync(sourceDirectory)) {
|
||||
const sourcePath = Path.join(sourceDirectory, entry)
|
||||
processSourcePath(sourcePath)
|
||||
}
|
||||
}
|
||||
/** Removes the MIT copyright notices from each source file in the given directory */
|
||||
export function removeNotices(sourceDirectory: string) {
|
||||
readDirectory(sourceDirectory)
|
||||
}
|
||||
38
task/build/package/build.ts
Normal file
38
task/build/package/build.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import { createPackageJsonRedirect } from './create-package-json-redirect'
|
||||
import { createPackageJson } from './create-package-json'
|
||||
|
||||
/** Builds package.json and redirect directories */
|
||||
export async function build(target: string) {
|
||||
console.log('building...package.json')
|
||||
const submodules = ['compiler', 'errors', 'parser', 'syntax', 'system', 'type', 'value']
|
||||
await createPackageJsonRedirect(target, submodules)
|
||||
await createPackageJson(target, submodules)
|
||||
}
|
||||
50
task/build/package/create-package-json-redirect.ts
Normal file
50
task/build/package/create-package-json-redirect.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import * as Fs from 'node:fs'
|
||||
|
||||
// prettier-ignore
|
||||
function writeRedirect(target: string, submodule: string) {
|
||||
Fs.mkdirSync(`${target}/${submodule}`, { recursive: true })
|
||||
Fs.writeFileSync(`${target}/${submodule}/package.json`,JSON.stringify({
|
||||
main: `../build/cjs/${submodule}/index.js`,
|
||||
types: `../build/cjs/${submodule}/index.d.ts`,
|
||||
}, null, 2))
|
||||
}
|
||||
// --------------------------------------------------------------------------------------------------------------------------
|
||||
// Builds redirect directories for earlier versions of Node. Note that TypeScript will use these directories to
|
||||
// resolve types when tsconfig.json is configured for `moduleResolution: 'Node16'`. This approach is referred to as
|
||||
// `package-json-redirect` and enables correct type resolution in lieu of a correct end user configuration.
|
||||
//
|
||||
// https://github.com/andrewbranch/example-subpath-exports-ts-compat/tree/main/examples/node_modules/package-json-redirects
|
||||
// --------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// prettier-ignore
|
||||
export function createPackageJsonRedirect(target: string, submodules: string[]) {
|
||||
submodules.forEach((submodule) => writeRedirect(target, submodule))
|
||||
}
|
||||
109
task/build/package/create-package-json.ts
Normal file
109
task/build/package/create-package-json.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import * as Fs from 'node:fs'
|
||||
import * as Path from 'node:path'
|
||||
|
||||
// prettier-ignore
|
||||
export function createPackageJson(target: string, submodules: string[]) {
|
||||
const content = JSON.stringify(resolvePackageJson(submodules), null, 2)
|
||||
const targetPath = Path.join(target, 'package.json')
|
||||
const targetDir = Path.dirname(targetPath)
|
||||
Fs.mkdirSync(targetDir, { recursive: true })
|
||||
Fs.writeFileSync(targetPath, content, 'utf-8')
|
||||
}
|
||||
// prettier-ignore
|
||||
function resolvePackageJson(submodules: string[]) {
|
||||
return {
|
||||
...resolveMetadata(),
|
||||
...resolveExports(submodules)
|
||||
}
|
||||
}
|
||||
// prettier-ignore
|
||||
function resolveSubmoduleExports(submodule: string) {
|
||||
return {
|
||||
require: {
|
||||
types: `./build/cjs/${submodule}/index.d.ts`,
|
||||
default: `./build/cjs/${submodule}/index.js`,
|
||||
},
|
||||
import: {
|
||||
types: `./build/esm/${submodule}/index.d.mts`,
|
||||
default: `./build/esm/${submodule}/index.mjs`,
|
||||
}
|
||||
}
|
||||
}
|
||||
// prettier-ignore
|
||||
function resolveExports(submodules: string[]) {
|
||||
const exports = submodules.reduce((acc, submodule) => {
|
||||
return { ...acc, [`./${submodule}`]: resolveSubmoduleExports(submodule) }
|
||||
}, {
|
||||
// ... and root module
|
||||
".": {
|
||||
"require": {
|
||||
"types": "./build/cjs/index.d.ts",
|
||||
"default": "./build/cjs/index.js",
|
||||
|
||||
},
|
||||
"import": {
|
||||
"types": "./build/esm/index.d.mts",
|
||||
"default": "./build/esm/index.mjs",
|
||||
}
|
||||
}
|
||||
})
|
||||
return { exports }
|
||||
}
|
||||
// prettier-ignore
|
||||
function resolveMetadata() {
|
||||
const packagePath = Path.join(process.cwd(), 'package.json')
|
||||
const packageJson = JSON.parse(Fs.readFileSync(packagePath, 'utf-8'))
|
||||
return {
|
||||
name: packageJson.name,
|
||||
version: packageJson.version,
|
||||
description: packageJson.description,
|
||||
keywords: packageJson.keywords,
|
||||
author: packageJson.author,
|
||||
license: packageJson.license,
|
||||
repository: packageJson.repository,
|
||||
// flagged by socket.dev if not present
|
||||
scripts: { test: 'echo test' },
|
||||
types: "./build/cjs/index.d.ts",
|
||||
main: "./build/cjs/index.js",
|
||||
module: "./build/esm/index.mjs",
|
||||
// disable auto bundle strategy: see https://github.com/esm-dev/esm.sh#bundling-strategy
|
||||
'esm.sh': { 'bundle': false },
|
||||
// specify modules with potential for side effects
|
||||
'sideEffects': [
|
||||
'./build/esm/type/registry/format.mjs',
|
||||
'./build/esm/type/registry/type.mjs',
|
||||
'./build/esm/type/system/policy.mjs',
|
||||
'./build/cjs/type/registry/format.js',
|
||||
'./build/cjs/type/registry/type.js',
|
||||
'./build/cjs/type/system/policy.js'
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user