Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move plugin setup script to development tools #2604

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/development-tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"postinstall.js"
],
"bin": {
"jbrowse-plugin-postinstall": "./postinstall.js"
"jbrowse-plugin-postinstall": "./postinstall.js",
"jbrowse-plugin-setup": "./setup.js"
},
"engines": {
"node": ">=10"
Expand All @@ -41,6 +42,7 @@
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"@rollup/plugin-typescript": "^8.3.0",
"@schemastore/package": "^0.0.6",
"babel-plugin-annotate-pure-calls": "^0.4.0",
"babel-plugin-dev-expression": "^0.2.3",
"babel-plugin-macros": "^3.1.0",
Expand Down
3 changes: 3 additions & 0 deletions packages/development-tools/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node

require('./dist/setup').main()
200 changes: 200 additions & 0 deletions packages/development-tools/src/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import fs from 'fs'
import os from 'os'
import path from 'path'
import chalk from 'chalk'
import { JSONSchemaForNPMPackageJsonFiles } from '@schemastore/package'

const fsPromises = fs.promises

type JSONValue =
| string
| number
| boolean
| null
| JSONValue[]
| { [key: string]: JSONValue }

export async function main() {
const packageJSONPath = resolvePath('package.json')
const packageJSON = await readJSON(packageJSONPath)
const rawPackageName = packageJSON.name
// ensure that yarn init has been run
if (rawPackageName === undefined) {
console.warn(
chalk.red(
'No name defined in package.json. Please run "yarn init" (or "npm init") before running "yarn setup" (or "npm run setup").',
),
)
process.exit(1)
}
if (rawPackageName === 'jbrowse-plugin-template') {
console.warn(
chalk.red(
'Please run "yarn init" (or "npm init") before running "yarn setup" (or "npm run setup").',
),
)
process.exit(1)
}

const packageName = getSafePackageName(rawPackageName)
const prefix = 'jbrowse-plugin-'
const pluginClassName = toPascalCase(
packageName.startsWith(prefix)
? packageName.slice(prefix.length)
: packageName,
)

updatePackageJSON(pluginClassName, packageJSON, packageJSONPath)
updateSrcIndex(pluginClassName)
updateJBrowseConfig(packageName, pluginClassName)
updateExampleFixture(packageName, pluginClassName)
updateReadme(rawPackageName, packageJSON.repository)
}

function updatePackageJSON(
pluginName: string,
packageJSON: JSONSchemaForNPMPackageJsonFiles,
packageJSONPath: string,
) {
// 1. Change "name" in the "jbrowse-plugin" and "config" fields to the name of your project (e.g. "MyProject")
packageJSON['jbrowse-plugin'].name = pluginName
if (!packageJSON.config) {
packageJSON.config = {}
}
packageJSON.config.jbrowse.plugin.name = pluginName

// this overwrites package.json
writeJSON(packageJSONPath, packageJSON)
}

// replace default plugin name in example plugin class
async function updateSrcIndex(pluginClassName: string) {
const indexFilePath = resolvePath(path.join('src', 'index.ts'))
let indexFile = await fsPromises.readFile(indexFilePath, 'utf-8')
indexFile = indexFile.replace(/TemplatePlugin/g, `${pluginClassName}Plugin`)
fsPromises.writeFile(indexFilePath, indexFile)
}

// replace default plugin name and url with project name and dist file
async function updateJBrowseConfig(packageName: string, pluginName: string) {
const jbrowseConfigPath = resolvePath('jbrowse_config.json')
const jbrowseConfig = await readJSON(jbrowseConfigPath)
jbrowseConfig.plugins[0].name = pluginName
jbrowseConfig.plugins[0].url = `http://localhost:9000/dist/${packageName}.umd.development.js`
writeJSON(jbrowseConfigPath, jbrowseConfig)
}

// replace default plugin name and url with project name and dist file
async function updateExampleFixture(packageName: string, pluginName: string) {
const fixtureLocation = resolvePath(
path.join('cypress', 'fixtures', 'hello_view.json'),
)
const exampleFixture = await readJSON(fixtureLocation)
exampleFixture.plugins[0].name = pluginName
exampleFixture.plugins[0].url = `http://localhost:9000/dist/${packageName}.umd.development.js`
writeJSON(fixtureLocation, exampleFixture)
}

async function updateReadme(
packageName: string,
repository: JSONSchemaForNPMPackageJsonFiles['repository'],
) {
// add status badge to README
const repoUrl = getUrlFromRepo(repository)
const readmePath = resolvePath('README.md')
const readmeLines = (await fsPromises.readFile(readmePath, 'utf-8')).split(
/\r?\n/,
)
if (readmeLines[0].startsWith(`# ${packageName}`)) {
return
}
readmeLines[0] = `# ${packageName}`
if (repoUrl !== undefined) {
readmeLines.unshift(
`![Integration](${repoUrl}/workflows/Integration/badge.svg?branch=main)${os.EOL}`,
)
}
fsPromises.writeFile(readmePath, readmeLines.join(os.EOL), 'utf8')
}

/*
****************************
Helpers
****************************
*/

async function writeJSON(path: string, data: JSONValue) {
let jsonString
try {
jsonString = JSON.stringify(data, null, 2)
} catch (error) {
console.error('There was a problem converting an object to JSON')
throw error
}
return fsPromises.writeFile(path, `${jsonString}\n`)
}

async function readJSON(path: string) {
let jsonString
try {
jsonString = await fsPromises.readFile(path, 'utf8')
} catch (error) {
console.error(`Could not read JSON file at ${path}`)
throw error
}
let jsonData
try {
jsonData = JSON.parse(jsonString)
} catch (error) {
console.error(
`Could not parse JSON file at ${path}, check for JSON syntax errors`,
)
throw error
}
return jsonData
}

// snagged from https://stackoverflow.com/a/53952925
function toPascalCase(string: string) {
return `${string}`
.replace(new RegExp(/[-_]+/, 'g'), ' ')
.replace(new RegExp(/[^\w\s]/, 'g'), '')
.replace(
new RegExp(/\s+(.)(\w+)/, 'g'),
($1, $2, $3) => `${$2.toUpperCase() + $3.toLowerCase()}`,
)
.replace(new RegExp(/\s/, 'g'), '')
.replace(new RegExp(/\w/), s => s.toUpperCase())
}

function getSafePackageName(name: string) {
return name
.toLowerCase()
.replace(/(^@.*\/)|((^[^a-zA-Z]+)|[^\w.-])|([^a-zA-Z0-9]+$)/g, '')
}

function getUrlFromRepo(repo: JSONSchemaForNPMPackageJsonFiles['repository']) {
if (repo === undefined) {
return repo
}
let url = undefined
if (typeof repo === 'string') {
url = repo
} else if (typeof repo === 'object') {
url = repo.url
}

if (typeof url === 'string') {
if (url.includes('github.com')) {
return url.replace(/\.git$/, '')
}
if (url.startsWith('github:')) {
return `https://github.com/${url.split(':')[1]}`
}
}
return undefined
}

function resolvePath(pathToResolve: string) {
return path.resolve(fs.realpathSync(process.cwd()), pathToResolve)
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3509,6 +3509,11 @@
estree-walker "^2.0.1"
picomatch "^2.2.2"

"@schemastore/package@^0.0.6":
version "0.0.6"
resolved "https://registry.yarnpkg.com/@schemastore/package/-/package-0.0.6.tgz#9a76713da1c7551293b7e72e4f387f802bfd5d81"
integrity sha512-uNloNHoyHttSSdeuEkkSC+mdxJXMKlcUPOMb//qhQbIQijXg8x54VmAw3jm6GJZQ5DBtIqGBd66zEQCDCChQVA==

"@sideway/address@^4.1.0":
version "4.1.2"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.2.tgz#811b84333a335739d3969cfc434736268170cad1"
Expand Down