import fs = require('fs')
import { Engine, JSFeature, VersionRange, VersionRangeMap, WhyNotMap, engines } from './index'
const jsFeatureString = (feature: string): string => {
return feature.replace(/([A-Z])/g, '-$1').slice(1).toLowerCase()
}
const simpleMap = (entries: [string, string][]) => {
let maxLength = 0
for (const [key] of entries) {
maxLength = Math.max(maxLength, key.length + 1)
}
return entries.map(([key, value]) => `\t${(key + ':').padEnd(maxLength)} ${value},`).join('\n')
}
const compareEngines = (a: Engine, b: Engine): number => {
const lowerA = a.toLowerCase()
const lowerB = b.toLowerCase()
return lowerA < lowerB ? -1 : lowerA > lowerB ? 1 : 0
}
const jsTableMap = (map: Partial<Record<Engine, VersionRange[]>>, whyNot: Partial<Record<Engine, string[]>>) => {
const lines: string[] = []
const whyNotKeys = (Object.keys(whyNot) as Engine[]).sort(compareEngines)
for (const engine of whyNotKeys) {
const failedTests = whyNot[engine]!
lines.push(`\t\t// Note: The latest version of ${JSON.stringify(engine)} failed ` + (failedTests.length === 1
? `this test: ${failedTests[0]}` : `${failedTests.length} tests including: ${failedTests[0]}`))
}
const engineKeys = (Object.keys(map) as Engine[]).sort(compareEngines)
const maxLength = engineKeys.reduce((a, b) => Math.max(a, b.length + 1), 0)
for (const engine of engineKeys) {
const items = map[engine]!.map(range => {
return `{start: v{${range.start.concat(0, 0).slice(0, 3).join(', ')
}}${range.end ? `, end: v{${range.end.concat(0, 0).slice(0, 3).join(', ')}}` : ''}}`
})
lines.push(`\t\t${(engine + ':').padEnd(maxLength)} {${items.join(', ')}},`)
}
if (lines.length === 0) return '{}'
return `{\n${lines.join('\n')}\n\t}`
}
const jsTableValidEnginesMap = (engines: Engine[]) => {
const maxLength = engines.reduce((a, b) => Math.max(a, b.length + 4), 0)
if (engines.length === 0) return '{}'
return engines.map(engine => {
return `\t${`"${engine.toLowerCase()}": `.padEnd(maxLength)}api.Engine${engine},`
}).join('\n')
}
const generatedByComment = `// This file was automatically generated by "js_table.ts"`
export const generateTableForJS = (map: VersionRangeMap<JSFeature>, whyNot: WhyNotMap<JSFeature>): void => {
const enginesKeys = (Object.keys(engines) as Engine[]).sort(compareEngines)
fs.writeFileSync(__dirname + '/../internal/compat/js_table.go',
`${generatedByComment}
package compat
type Engine uint8
const (
${enginesKeys.map((engine, i) => `\t${engine}${i ? '' : ' Engine = iota'}`).join('\n')}
)
func (e Engine) String() string {
\tswitch e {
${enginesKeys.map(engine => `\tcase ${engine}:\n\t\treturn "${engine.toLowerCase()}"`).join('\n')}
\t}
\treturn ""
}
func (e Engine) IsBrowser() bool {
\tswitch e {
\tcase Chrome, Edge, Firefox, IE, IOS, Opera, Safari:
\t\treturn true
\t}
\treturn false
}
type JSFeature uint64
const (
${Object.keys(map).sort().map((feature, i) => `\t${feature}${i ? '' : ' JSFeature = 1 << iota'}`).join('\n')}
)
var StringToJSFeature = map[string]JSFeature{
${simpleMap(Object.keys(map).sort().map(feature => [`"${jsFeatureString(feature)}"`, feature]))}
}
func (features JSFeature) Has(feature JSFeature) bool {
\treturn (features & feature) != 0
}
func (features JSFeature) ApplyOverrides(overrides JSFeature, mask JSFeature) JSFeature {
\treturn (features & ^mask) | (overrides & mask)
}
var jsTable = map[JSFeature]map[Engine][]versionRange{
${Object.keys(map).sort().map(feature => `\t${feature}: ${jsTableMap(map[feature as JSFeature]!, whyNot[feature as JSFeature]!)},`).join('\n')}
}
// Return all features that are not available in at least one environment
func UnsupportedJSFeatures(constraints map[Engine]Semver) (unsupported JSFeature) {
\tfor feature, engines := range jsTable {
\t\tif feature == InlineScript {
\t\t\tcontinue // This is purely user-specified
\t\t}
\t\tfor engine, version := range constraints {
\t\t\tif versionRanges, ok := engines[engine]; !ok || !isVersionSupported(versionRanges, version) {
\t\t\t\tunsupported |= feature
\t\t\t}
\t\t}
\t}
\treturn
}
`)
fs.writeFileSync(__dirname + '/../pkg/api/api_js_table.go',
`${generatedByComment}
package api
import "github.com/evanw/esbuild/internal/compat"
type EngineName uint8
const (
${enginesKeys.filter(engine => engine !== 'ES').map((engine, i) => `\tEngine${engine}${i ? '' : ' EngineName = iota'}`).join('\n')}
)
func convertEngineName(engine EngineName) compat.Engine {
\tswitch engine {
${enginesKeys.filter(engine => engine !== 'ES').map(engine => `\tcase Engine${engine}:\n\t\treturn compat.${engine}`).join('\n')}
\tdefault:
\t\tpanic("Invalid engine name")
\t}
}
`)
fs.writeFileSync(__dirname + '/../pkg/cli/cli_js_table.go',
`${generatedByComment}
package cli
import "github.com/evanw/esbuild/pkg/api"
var validEngines = map[string]api.EngineName{
${jsTableValidEnginesMap(enginesKeys.filter(engine => engine !== 'ES'))}
}
`)
}