UNPKG

generator-luau

Version:
333 lines (287 loc) 8.85 kB
import Generator from 'yeoman-generator'; import path from 'path'; import { format } from 'prettier-package-json'; import axios from 'axios'; class PackageJsonBuilder { constructor(name) { this._base = { name, keywords: ['luau'], }; this._dependencies = {}; this._devDependencies = {}; this._scripts = {}; } static from(packageJson) { const { name, ...definitions } = packageJson; return new PackageJsonBuilder(name).merge(definitions) } merge({ scripts, dependencies, devDependencies, ...definitions }) { Object.assign(this._base, definitions); if (scripts) { this.addScripts(scripts); } if (devDependencies) { this.addDevDependencies(devDependencies); } if (dependencies) { this.addDependencies(dependencies); } return this } addScripts(scripts) { Object.assign(this._scripts, scripts); return this } addDevDependencies(dependencies) { Object.assign(this._devDependencies, dependencies); return this } addDependencies(dependencies) { Object.assign(this._dependencies, dependencies); return this } build() { return Object.assign( {}, { scripts: this._scripts, dependencies: this._dependencies, devDependencies: this._devDependencies, }, this._base ) } } const VERSION_CACHE = { '@jsdotlua/jest': '3.10.0', '@jsdotlua/jest-globals': '3.10.0', npmluau: '0.1.2', }; function fetchScopedPackageVersion(packageName) { const scope = packageName.substring(1, packageName.indexOf('/')); return axios .get(`https://registry.npmjs.com/-/v1/search`, { params: { text: `scope:${scope} ${packageName}`, }, }) .then((res) => res.data?.objects[0]?.package?.version) } function fetchPackageVersion(packageName) { return axios .get(`https://registry.npmjs.org/${packageName}/latest`) .then((res) => res.data?.version) } async function fetchLatestNpmPackageVersion(packageName) { const version = VERSION_CACHE[packageName]; if (version !== undefined) { return version } const fetchedVersion = ( packageName.startsWith('@') ? fetchScopedPackageVersion(packageName) : fetchPackageVersion(packageName) ).catch((err) => { console.warn(`fetching '${packageName}' version on npm errored: ${err}`); return null }); if (fetchedVersion) { VERSION_CACHE[packageName] = fetchedVersion; } return fetchedVersion } async function createDependencies(...packages) { const versions = await Promise.all( packages.map(async (name) => ({ name, version: await fetchLatestNpmPackageVersion(name), })) ); return versions.reduce((dependencies, { name, version }) => { if (version) { dependencies[name] = '^' + version; } else { console.warn( `skip dependency '${name}'. You can manually add it with ` + `'npm install ${name}' or 'yarn add ${name}'` ); } return dependencies }, {}) } const extractPackageName = (packageName) => { const firstSlash = packageName.indexOf('/'); if (firstSlash) { return { name: packageName.slice(firstSlash + 1), scope: packageName.slice(0, firstSlash), full: packageName, } } return { name: packageName, scope: null, full: packageName, } }; class LuauPackageGenerator extends Generator { constructor(args, opts) { super(args, opts); this._name = extractPackageName(opts.name); this._authorName = opts.authorName; this._authorEmail = opts.authorEmail; this._githubOwner = opts.githubOwner; this._luaExtension = opts.luaExtension; this._useJest = opts.useJest; this._luaEnvironment = opts.luaEnvironment; this._licenseFileName = opts.licenseFileName; this._location = opts.location; this._isWorkspace = opts.location != null; this._canSetupMultipleProjects = this._isWorkspace; } async prompting() { this._projects = [ await this._setupProject( this._name.full, path.basename(this.destinationRoot()) ), ]; let askAgain = this._canSetupMultipleProjects; while (askAgain) { const { setupNewProject } = await this.prompt([ { type: 'confirm', name: 'setupNewProject', message: 'Would you like to setup another project?', default: false, }, ]); if (setupNewProject) { this._projects.push(await this._setupProject()); } else { askAgain = false; } } this.packages = this._projects.map(({ projectName }) => projectName); } async _setupProject(name, defaultName) { const { projectName } = name ? { projectName: name } : await this.prompt([ { type: 'input', name: 'projectName', message: 'What is the project name?', default: defaultName, }, ]); const { useReact } = await this.prompt([ { type: 'confirm', name: 'useReact', message: 'Would you like to use React Lua?', when: this._luaEnvironment === 'roblox', default: false, }, ]); return { projectName, useReact, } } async writing() { const luaExtension = this._luaExtension; const luaEnvironment = this._luaEnvironment; const licenseName = this.licenseName ?? 'UNLICENSED'; await Promise.all( this._projects.map(async ({ projectName, useReact }) => { const projectNameInfo = extractPackageName(projectName); const getLocation = this._location ? (...subpath) => this.destinationPath( this._location, projectNameInfo.name, ...subpath ) : (...subpath) => this.destinationPath(...subpath); const packageJsonLocation = getLocation('package.json'); const packageBuilder = this.fs.exists(packageJsonLocation) ? PackageJsonBuilder.from(this.fs.readJSON(packageJsonLocation)) : new PackageJsonBuilder(projectNameInfo.full); const repositoryBaseUrl = this._isWorkspace ? `https://github.com/${this._githubOwner}/${this._name.name}` : `https://github.com/${this._githubOwner}/${projectNameInfo.name}`; const gitRepoUrl = `git+${repositoryBaseUrl}.git`; const entryPoint = `src/init.${luaExtension}`; packageBuilder.merge({ version: '0.1.0', main: entryPoint, description: '', author: `${this._authorName} <${this._authorEmail}>`, repository: this._isWorkspace ? { type: 'git', url: gitRepoUrl, directory: `${this._location}/${projectNameInfo.name}`, } : { type: 'git', url: gitRepoUrl }, homepage: `${repositoryBaseUrl}#readme`, }); this.fs.write(getLocation(entryPoint), 'return {}\n'); this.fs.copyTpl( this.templatePath('README.md'), getLocation('README.md'), { projectName: projectNameInfo.name, fullProjectName: projectNameInfo.full, repoName: this._name.name, licenseName, licenseFileName: this._licenseFileName, licensePath: `${this._location ? '../../' : ''}${this._licenseFileName}`, githubOwner: this._githubOwner, } ); const copyFiles = { 'CHANGELOG.md': 'CHANGELOG.md', npm_ignore: '.npmignore', }; Object.entries(copyFiles).forEach(([templateName, destinationName]) => { this.fs.copyTpl( this.templatePath(templateName), this.destinationPath(destinationName) ); }); if (useReact) { if (luaEnvironment === 'roblox') { packageBuilder.addDependencies( await createDependencies( '@jsdotlua/react', '@jsdotlua/react-roblox' ) ); } else { packageBuilder.addDependencies( await createDependencies('@jsdotlua/react') ); } } if (this._useJest) { packageBuilder.addDevDependencies( await createDependencies('@jsdotlua/jest', '@jsdotlua/jest-globals') ); const testFilePath = `init.test.${luaExtension}`; this.fs.copyTpl( this.templatePath('init.test.lua'), getLocation(`src/__tests__/${testFilePath}`) ); } this.fs.write( packageJsonLocation, format(packageBuilder.build()) ); }) ); } } export { LuauPackageGenerator as default };