blob: 5a3d80938bdf6daca3b1f9a6ff8fd22b8f92fb22 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// @ts-nocheck <-- This is needed as this file is basically a JavaScript script
// but with some TypeScript niceness baked in
const path = require('path')
const fs = require('fs')
const { glob } = require('glob')
const concurrently = require('concurrently')
const child_process = require('child_process')
const jsoncParse = require('jsonc-parser').parse
const packageData = jsoncParse(
fs.readFileSync(path.resolve('package.json'), 'utf8')
)
const pkg_version = packageData['version']
function rmFileOrDirectory(path) {
if (fs.existsSync(path)) fs.rmSync(path, { recursive: true })
}
function genVersionTS() {
const version = JSON.stringify(require('../package.json').version).replace(
/"/g,
"'"
)
fs.writeFileSync(
'./src/version.ts',
`/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const LIB_VERSION = ${version}
`
)
}
const nodeclean = () => ['out', 'dist'].forEach((dir) => rmFileOrDirectory(dir))
const scalaclean = () =>
glob('**/target', { cwd: '.' })
.then((files) => files.forEach((dir) => rmFileOrDirectory(dir)))
.catch((err) => console.log(err))
function watch() {
concurrently(
['yarn watch:vite-dev', 'yarn watch:svelte', 'yarn watch:tdmlEditorJS'],
{
killOthersOn: ['failure'],
restartTries: 1,
}
)
}
function package() {
const pkg_dir = 'dist/package'
// create .vscodeignore to not package all node_modules into the vsix
fs.writeFileSync(
path.join(pkg_dir, '.vscodeignore'),
`# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
**/node_modules/**/*
!node_modules/
!node_modules/**/*
`
)
}
function packageNamePath(packageName) {
return path.join(...packageName.split('/'))
}
function readPackageVersion(packageRoot) {
const packageJsonPath = path.join(packageRoot, 'package.json')
if (!fs.existsSync(packageJsonPath)) return undefined
try {
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')).version
} catch (err) {
console.warn(
`[omega-edit] Unable to read package version for ${packageRoot}: ${String(
err
)}`
)
return undefined
}
}
function shouldPatchOmegaEditPackage(packageRoot, expectedVersion, label) {
const version = readPackageVersion(packageRoot)
if (version === expectedVersion) {
return true
}
const versionLabel = version ?? 'unknown'
console.warn(
`[omega-edit] Skipping ${label} patch for ${packageRoot}; expected ${expectedVersion}, found ${versionLabel}.`
)
return false
}
function patchOmegaEditClientLogger(
packageRoot = 'node_modules/@omega-edit/client'
) {
if (!shouldPatchOmegaEditPackage(packageRoot, '2.0.0', 'client logger')) {
return
}
const loggerTargets = [
path.join(packageRoot, 'dist/cjs/logger.js'),
path.join(packageRoot, 'dist/esm/logger.js'),
]
const transportPattern =
/setLogger\(buildLogger\(pino(?:_1\.default)?\.transport\(\{[\s\S]*?\}\)\)\);/
loggerTargets.forEach((loggerPath) => {
if (!fs.existsSync(loggerPath)) {
console.warn(`[omega-edit] Client logger not found: ${loggerPath}`)
return
}
const source = fs.readFileSync(loggerPath, 'utf-8')
const patched = source.replace(
transportPattern,
'setLogger(buildLogger(process.stderr));'
)
if (patched === source) {
if (!source.includes('setLogger(buildLogger(process.stderr));')) {
console.warn(
`[omega-edit] Unable to patch OmegaEdit client logger at ${loggerPath}; leaving upstream source unchanged.`
)
}
return
}
fs.writeFileSync(loggerPath, patched, 'utf-8')
})
}
function patchOmegaEditServerLocator(searchRoot = 'node_modules') {
const serverTargets = glob.sync('**/@omega-edit/server/out/index.js', {
cwd: searchRoot,
absolute: true,
nodir: true,
})
const buggyLocator = '.replace("node_modules","")'
const knownFixedLocators = [
'.slice(0,-"node_modules".length)',
'.slice(0,-12)',
]
if (serverTargets.length === 0) {
return
}
serverTargets.forEach((serverPath) => {
const packageRoot = path.dirname(path.dirname(serverPath))
if (!shouldPatchOmegaEditPackage(packageRoot, '2.0.0', 'server locator')) {
return
}
const source = fs.readFileSync(serverPath, 'utf-8')
const patched = source.replaceAll(buggyLocator, knownFixedLocators[0])
if (patched === source) {
if (!knownFixedLocators.some((locator) => source.includes(locator))) {
console.warn(
`[omega-edit] Unable to patch OmegaEdit server locator at ${serverPath}; leaving upstream source unchanged.`
)
}
return
}
fs.writeFileSync(serverPath, patched, 'utf-8')
})
}
function patchOmegaEditRuntime(
packageRoot = 'node_modules/@omega-edit/client',
searchRoot = 'node_modules'
) {
patchOmegaEditClientLogger(packageRoot)
patchOmegaEditServerLocator(searchRoot)
}
function copyPackageRuntimeTree(
packageName,
sourcePackageDir,
destinationPackageDir,
seen = new Set()
) {
const visitKey = `${sourcePackageDir}|${destinationPackageDir}`
if (seen.has(visitKey)) return
seen.add(visitKey)
if (!fs.existsSync(sourcePackageDir)) {
throw new Error(
`Package source not found for ${packageName}: ${sourcePackageDir}`
)
}
rmFileOrDirectory(destinationPackageDir)
fs.mkdirSync(path.dirname(destinationPackageDir), { recursive: true })
fs.cpSync(sourcePackageDir, destinationPackageDir, {
recursive: true,
force: true,
})
const packageJsonPath = path.join(sourcePackageDir, 'package.json')
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
const dependencies = Object.keys(packageJson.dependencies || {})
if (dependencies.length === 0) return
const destinationNodeModulesDir = path.join(
destinationPackageDir,
'node_modules'
)
dependencies.forEach((dependencyName) => {
const sourceDependencyDirCandidates = [
path.join(
sourcePackageDir,
'node_modules',
packageNamePath(dependencyName)
),
path.join('node_modules', packageNamePath(dependencyName)),
]
const sourceDependencyDir = sourceDependencyDirCandidates.find(
(candidate) => fs.existsSync(candidate)
)
if (!sourceDependencyDir) {
throw new Error(
`Unable to resolve runtime dependency ${dependencyName} for ${packageName}`
)
}
copyPackageRuntimeTree(
dependencyName,
sourceDependencyDir,
path.join(destinationNodeModulesDir, packageNamePath(dependencyName)),
seen
)
})
}
function syncOmegaEditClientRuntime() {
const clientPackageName = '@omega-edit/client'
const sourceClientDir = path.join(
'node_modules',
packageNamePath(clientPackageName)
)
const destinationClientDir = path.join(
'dist/package/node_modules',
packageNamePath(clientPackageName)
)
patchOmegaEditRuntime(sourceClientDir, 'node_modules')
copyPackageRuntimeTree(
clientPackageName,
sourceClientDir,
destinationClientDir
)
patchOmegaEditRuntime(destinationClientDir, 'dist/package/node_modules')
}
function packageVsix() {
const vsceCommand =
process.platform === 'win32'
? path.resolve('node_modules', '.bin', 'vsce.cmd')
: path.resolve('node_modules', '.bin', 'vsce')
const result = child_process.spawnSync(
vsceCommand,
['package', '--out', '../../'],
{
cwd: 'dist/package',
stdio: 'inherit',
shell: process.platform === 'win32',
}
)
if (result.error) {
console.error(result.error)
process.exit(1)
}
process.exit(result.status === null ? 1 : result.status)
}
function getScalaVersions() {
const scalaVersions = ['2.12', '2.13']
// The scala 3 version of the debugger should only exist if JDK >= 17 is being used
if (fs.existsSync(`debugger/target/jvm-3/universal/stage`)) {
scalaVersions.push('3')
}
return scalaVersions
}
function moveDebuggers() {
getScalaVersions().forEach(async (scalaVersion) => {
const serverPackage = `daffodil-debugger-${scalaVersion}-${pkg_version}`
const jvmFolderName = `jvm-${scalaVersion}`
const stageFilePath = path.resolve(
`debugger/target/${jvmFolderName}/universal/stage`
)
const serverPackageFolder = path.join('dist/debuggers', serverPackage)
// remove debugger package folder if exists
if (fs.existsSync(serverPackageFolder)) {
fs.rmSync(serverPackageFolder, { recursive: true, force: true })
}
// Copy staged debugger files to desired location
fs.cpSync(stageFilePath, serverPackageFolder, { recursive: true })
})
}
/* START SECTION: Update version */
// helper function to get the version passed in
function parseArgs() {
const args = process.argv.slice(1)
for (let i = 0; i < args.length; i++) {
if ((args[i] === '-v' || args[i] == '--version') && i + 1 < args.length) {
return args[i + 1]
}
}
return null
}
function updatePackageJsonVersion(version) {
const packageJsonPath = 'package.json'
if (!fs.existsSync(packageJsonPath)) {
console.error('Error: package.json not found')
process.exit(1)
}
const pkgRaw = fs.readFileSync(packageJsonPath, 'utf-8')
const pkg = JSON.parse(pkgRaw)
pkg.version = version
fs.writeFileSync(
packageJsonPath,
JSON.stringify(pkg, null, 2) + '\n',
'utf-8'
)
console.log(`package.json version updated to ${version}`)
}
function updateVersionFile(version) {
fs.writeFileSync('VERSION', version + '\n', 'utf-8')
console.log(`VERSION updated to ${version}`)
}
function updateVersion() {
let newVersion = parseArgs()
if (!newVersion) {
console.error('Error: Please provide a version using -v or --version')
process.exit(1)
}
if (newVersion.startsWith('v')) {
newVersion = newVersion.slice(1)
}
updatePackageJsonVersion(newVersion)
updateVersionFile(newVersion)
}
/* END SECTION: Update version */
/* START SECTION: LICENSE related methods */
function getLicenseData() {
return JSON.parse(
child_process
.execSync(
`yarn licenses list --json --prod | jq 'select(.type == "table").data.body'`
)
.toString()
)
}
function checkMissingLicenseData() {
const licenses = getLicenseData()
const packageLicenseData = fs.readFileSync('build/package/LICENSE', 'utf-8')
const packageLicenseLines = packageLicenseData.split('\n')
const packageNoticeData = fs.readFileSync('build/package/NOTICE', 'utf-8')
const packageNoticeLines = packageNoticeData.split('\n')
const packageNoNoticeData = fs.readFileSync('build/package/NONOTICE', 'utf-8')
const packageNoNoticeLines = packageNoNoticeData.split('\n')
const missingLicenseData = []
licenses.forEach((license) => {
if (
packageLicenseLines
.map((line) => line.startsWith('-') && line.includes(license[0]))
.some(Boolean) ||
packageNoticeLines
.map((line) => line.startsWith('-') && line.includes(license[0]))
.some(Boolean) ||
packageNoNoticeLines
.map((line) => line.startsWith('-') && line.includes(license[0]))
.some(Boolean)
)
return
missingLicenseData.push(license[0])
})
if (missingLicenseData.length > 0) {
console.log('Missing LICENSE data for the following dependencies')
missingLicenseData.forEach((licenseName) =>
console.log(` - ${licenseName}`)
)
process.exit(1)
} else {
console.log('No missing LICENSE data!')
}
}
function checkLicenseCompatibility() {
const licenses = getLicenseData()
const licenseDataFileData = JSON.parse(
fs.readFileSync('build/license_data.json', 'utf-8')
)
const allowedLicenseTypes = licenseDataFileData.allowedLiceneseTypes
const allowedLicenses = licenseDataFileData.packages
.filter((data) => data.allowed)
.map((data) => data.name)
const badLicenses = []
licenses.forEach((license) => {
const licenseCompatibile =
allowedLicenseTypes.filter((licenseType) =>
license[2].includes(licenseType)
).length > 0
? true
: false
if (!licenseCompatibile && !allowedLicenses.includes(license[0])) {
badLicenses.push(license)
}
})
if (badLicenses.length > 0) {
console.log('The follow dependencies have not ASF approved LICENCES:')
badLicenses.forEach((license) =>
console.log(` - ${license[0]} -> ${license[2]}`)
)
process.exit(1)
} else {
console.log('All dependency LICENSES are compatibile!')
}
}
/* END SECTION: LICENSE related methods */
module.exports = {
genVersionTS: genVersionTS,
nodeclean: nodeclean,
scalaclean: scalaclean,
updateVersion: updateVersion,
watch: watch,
package: package,
patchOmegaEditClientLogger: patchOmegaEditClientLogger,
patchOmegaEditServerLocator: patchOmegaEditServerLocator,
patchOmegaEditRuntime: patchOmegaEditRuntime,
syncOmegaEditClientRuntime: syncOmegaEditClientRuntime,
packageVsix: packageVsix,
checkMissingLicenseData: checkMissingLicenseData,
checkLicenseCompatibility: checkLicenseCompatibility,
moveDebuggers: moveDebuggers,
}