diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b88c9ff..bc07b2d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,6 +81,8 @@ jobs: - defaults with npm - defaults with pnpm - try-scenarios + - no-build with npm + - no-build with pnpm steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 diff --git a/index.js b/index.js index ed1e5b9..55ae228 100644 --- a/index.js +++ b/index.js @@ -11,8 +11,8 @@ function stringifyAndNormalize(contents) { } const replacers = { - 'package.json'(content) { - return this.updatePackageJson(content); + 'package.json'(locals, content) { + return this.updatePackageJson(locals, content); }, }; @@ -34,6 +34,9 @@ module.exports = { ); } + // noBuild doesn't exist because of how ember-cli normalizes args + let noBuild = options.build === false; + return { name: options.name, blueprintVersion: require('./package.json').version, @@ -51,8 +54,11 @@ module.exports = { '--pnpm': isPnpm(options), '--npm': isNpm(options), '--typescript': options.typescript, + '--no-build': options.noBuild, }), ciProvider: options.ciProvider, + noBuild, + hasBuild: !noBuild, }; }, @@ -77,12 +83,44 @@ module.exports = { ); } + if (options.noBuild) { + files = files.filter((filename) => { + if (filename.endsWith('rollup.config.mjs')) return false; + if (filename.endsWith('babel.publish.config.cjs')) return false; + if (filename.endsWith('tsconfig.publish.json')) return false; + + return true; + }); + } + return files; }, - updatePackageJson(content) { + updatePackageJson(locals, content) { let contents = JSON.parse(content); - return stringifyAndNormalize(sortPackageJson(contents)); + + if (locals.noBuild) { + let extSuffix = locals.typescript ? 'ts' : 'js'; + + contents.exports = { + '.': `./src/index.${extSuffix}`, + './addon-main.js': './addon-main.cjs', + './*': './src/*', + './*.css': './src/*.css', + }; + + delete contents.scripts.build; + delete contents.scripts.prepack; + delete contents.devDependencies['@embroider/addon-dev']; + delete contents.devDependencies['@ember/library-tsconfig']; + delete contents.devDependencies['rollup']; + + contents.scripts['lint:publish'] = 'publint run --level error'; + } + + let sorted = sortPackageJson(contents); + let normalized = stringifyAndNormalize(sorted); + return normalized; }, /** @@ -100,11 +138,11 @@ module.exports = { * _js_eslint.config.mjs is deleted * _ts_eslint.config.mjs is renamed to eslint.config.mjs */ - buildFileInfo(_intoDir, _templateVariables, file, _commandOptions) { + buildFileInfo(_intoDir, locals, file, _commandOptions) { let fileInfo = this._super.buildFileInfo.apply(this, arguments); if (file in replacers) { - fileInfo.replacer = replacers[file].bind(this); + fileInfo.replacer = replacers[file].bind(this, locals); } return fileInfo; diff --git a/tests/smoke-tests/--no-build.test.ts b/tests/smoke-tests/--no-build.test.ts new file mode 100644 index 0000000..6400f9f --- /dev/null +++ b/tests/smoke-tests/--no-build.test.ts @@ -0,0 +1,125 @@ +import path, { join } from 'node:path'; +import tmp from 'tmp-promise'; +import fs from 'node:fs/promises'; +import fixturify from 'fixturify'; +import { execa } from 'execa'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; + +import { + assertGeneratedCorrectly, + filesMatching, + SUPPORTED_PACKAGE_MANAGERS, + safeExecaEnv, +} from '../helpers.js'; +const blueprintPath = path.join(__dirname, '../..'); +let localEmberCli = require.resolve('ember-cli/bin/ember'); + +for (let packageManager of SUPPORTED_PACKAGE_MANAGERS) { + describe(`--no-build with ${packageManager}`, () => { + let tmpDir: string; + let addonDir: string; + let addonName = 'my-addon'; + + async function commandSucceeds(command: string) { + let result = await execa({ + cwd: addonDir, + shell: true, + preferLocal: true, + extendEnv: false, + env: safeExecaEnv(), + // Allows us to not fail yet when the command fails + // but we'd still fail appropriately with the exitCode check below. + // When we fail, we want to check for git diffs for debugging purposes. + reject: false, + })(command); + + if (result.exitCode !== 0) { + console.log(result); + console.log(`\n\n${command} exited with code ${result.exitCode}\n\n`); + console.log(result.stdout); + console.log(result.stderr); + console.log(`\n\n git diff \n\n`); + await execa({ cwd: addonDir, stdio: 'inherit' })`git diff`; + } + + expect(result.exitCode, `\`${command}\` succeeds`).toEqual(0); + + return result; + } + + beforeAll(async () => { + tmpDir = (await tmp.dir()).path; + addonDir = join(tmpDir, addonName); + await execa({ + cwd: tmpDir, + extendEnv: false, + })`${localEmberCli} addon ${addonName} -b ${blueprintPath} --skip-npm true --${packageManager} --no-build`; + // Have to use --force because NPM is *stricter* when you use tags in package.json + // than pnpm (in that tags don't match any specified stable version) + if (packageManager === 'npm') { + await execa({ cwd: addonDir, extendEnv: false })`npm install --force`; + } else if (packageManager === 'pnpm') { + await execa({ cwd: addonDir, extendEnv: false })`pnpm install`; + } + }); + + it('was generated correctly', async () => { + assertGeneratedCorrectly({ + projectRoot: addonDir, + packageManager, + typeScript: false, + }); + }); + + // Tests are additive, so when running them in order, we want to check linting + // before we add files from fixtures + it('lints pass', async () => { + await commandSucceeds(`${packageManager} run lint`); + }); + + // Tests are additive, so when running them in order, we want to check linting + // before we add files from fixtures + it('lints with no fixtures all pass', async () => { + let { exitCode } = await execa({ cwd: addonDir, extendEnv: false })`pnpm lint`; + + expect(exitCode).toEqual(0); + }); + + it('lint:fix with no fixtures', async () => { + let { exitCode } = await execa({ + cwd: addonDir, + extendEnv: false, + })`${packageManager} run lint:fix`; + + expect(exitCode).toEqual(0); + }); + + describe('with fixture', () => { + beforeEach(async () => { + let addonFixture = fixturify.readSync('./fixtures/addon'); + fixturify.writeSync(join(addonDir, 'src'), addonFixture); + + let testFixture = fixturify.readSync('./fixtures/rendering-tests'); + fixturify.writeSync(join(addonDir, 'tests/rendering'), testFixture); + + fixturify.writeSync( + join(addonDir, 'tests/unit'), + fixturify.readSync('./fixtures/build-mode-tests'), + ); + }); + + it('lint:fix', async () => { + await commandSucceeds(`${packageManager} run lint:fix`); + }); + + it('test', async () => { + let testResult = await commandSucceeds(`${packageManager} run test`); + + console.log(testResult.stdout); + expect(testResult.stdout).to.include('# tests 6'); + expect(testResult.stdout).to.include('# pass 6'); + expect(testResult.stdout).to.include('# fail 0'); + }); + }); + }); +}