From 1ff74996c4481801ac540c5e79d360c2a3f8d3c7 Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Tue, 25 Apr 2023 20:31:54 -0700 Subject: [PATCH] change to esm and add a template test --- packages/create-redwood-app/build.mjs | 1 + packages/create-redwood-app/package.json | 4 +- .../src/create-redwood-app.js | 19 +++- packages/create-redwood-app/src/telemetry.js | 10 +- .../create-redwood-app/tests/template.test.js | 102 ++++++++++++++++++ 5 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 packages/create-redwood-app/tests/template.test.js diff --git a/packages/create-redwood-app/build.mjs b/packages/create-redwood-app/build.mjs index 9fbf8d0a0ab9..611e567dd0a4 100644 --- a/packages/create-redwood-app/build.mjs +++ b/packages/create-redwood-app/build.mjs @@ -8,6 +8,7 @@ import * as esbuild from 'esbuild' // instead of using the catch-all `packages: 'external'` option. const result = await esbuild.build({ entryPoints: ['src/create-redwood-app.js'], + format: 'esm', bundle: true, platform: 'node', target: ['node18'], diff --git a/packages/create-redwood-app/package.json b/packages/create-redwood-app/package.json index 34c4b4407558..d207d7b72e5c 100644 --- a/packages/create-redwood-app/package.json +++ b/packages/create-redwood-app/package.json @@ -1,6 +1,7 @@ { "name": "create-redwood-app", "version": "5.0.0", + "type": "module", "repository": { "type": "git", "url": "https://github.com/redwoodjs/redwood.git", @@ -14,8 +15,7 @@ ], "scripts": { "build": "yarn node ./build.mjs", - "build:watch": "nodemon --watch src --ignore dist,template --exec \"yarn build\"", - "prepublishOnly": "NODE_ENV=production yarn build" + "test": "yarn node --test" }, "dependencies": { "@opentelemetry/api": "1.4.1", diff --git a/packages/create-redwood-app/src/create-redwood-app.js b/packages/create-redwood-app/src/create-redwood-app.js index b26fa8774668..3c364b17ff48 100644 --- a/packages/create-redwood-app/src/create-redwood-app.js +++ b/packages/create-redwood-app/src/create-redwood-app.js @@ -1,6 +1,7 @@ #!/usr/bin/env node -import path from 'path' +import path from 'node:path' +import { fileURLToPath } from 'node:url' import { trace, SpanStatusCode } from '@opentelemetry/api' import chalk from 'chalk' @@ -14,13 +15,21 @@ import yargs from 'yargs/yargs' import { RedwoodTUI, ReactiveTUIContent, RedwoodStyling } from '@redwoodjs/tui' -import { name, version } from '../package' - +// In ESM, for relative paths, the extension is important. +// See https://nodejs.org/dist/latest-v18.x/docs/api/esm.html#mandatory-file-extensions. import { startTelemetry, shutdownTelemetry, recordErrorViaTelemetry, -} from './telemetry' +} from './telemetry.js' + +// JSON modules aren't stable yet, but if they were we could use them instead. +// See https://nodejs.org/dist/latest-v18.x/docs/api/esm.html#json-modules. +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +const { name, version } = fs.readJSONSync( + path.resolve(__dirname, '../package.json') +) // Telemetry const { telemetry } = Parser(hideBin(process.argv)) @@ -152,7 +161,7 @@ async function executeCompatibilityCheck(templateDir, yarnInstall) { */ function checkNodeAndYarnVersion(templateDir) { return new Promise((resolve) => { - const { engines } = require(path.join(templateDir, 'package.json')) + const { engines } = fs.readJSONSync(path.join(templateDir, 'package.json')) checkNodeVersionCb(engines, (_error, result) => { return resolve([result.isSatisfied, result.versions]) diff --git a/packages/create-redwood-app/src/telemetry.js b/packages/create-redwood-app/src/telemetry.js index c72028852088..33be1bcad10e 100644 --- a/packages/create-redwood-app/src/telemetry.js +++ b/packages/create-redwood-app/src/telemetry.js @@ -1,3 +1,6 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + import { diag, DiagConsoleLogger, DiagLogLevel } from '@opentelemetry/api' import opentelemetry, { SpanStatusCode } from '@opentelemetry/api' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' @@ -9,9 +12,14 @@ import { import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' import ci from 'ci-info' import envinfo from 'envinfo' +import fs from 'fs-extra' import system from 'systeminformation' -import { name as packageName, version as packageVersion } from '../package' +// JSON modules aren't stable yet, but if they were we could use them instead. +// See https://nodejs.org/dist/latest-v18.x/docs/api/esm.html#json-modules. +const { name: packageName, version: packageVersion } = fs.readJSONSync( + path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../package.json') +) /** * @type NodeTracerProvider diff --git a/packages/create-redwood-app/tests/template.test.js b/packages/create-redwood-app/tests/template.test.js new file mode 100644 index 000000000000..95ece6742eca --- /dev/null +++ b/packages/create-redwood-app/tests/template.test.js @@ -0,0 +1,102 @@ +/* eslint-env node, es2022 */ + +import assert from 'node:assert' +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import { test } from 'node:test' + +const TEMPLATE_PATH = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../template' +) + +// If you add, move, or remove a file from the create-redwood-app template, +// you'll have to update this test. +test("the file structure hasn't unintentionally changed", () => { + assert.deepStrictEqual(walk(TEMPLATE_PATH), [ + '/.editorconfig', + '/.env', + '/.env.defaults', + '/.env.example', + '/.nvmrc', + '/.vscode/extensions.json', + '/.vscode/launch.json', + '/.vscode/settings.json', + '/.yarn/releases/yarn-3.5.0.cjs', + '/.yarnrc.yml', + '/README.md', + '/api/db/schema.prisma', + '/api/jest.config.js', + '/api/package.json', + '/api/server.config.js', + '/api/src/directives/requireAuth/requireAuth.test.ts', + '/api/src/directives/requireAuth/requireAuth.ts', + '/api/src/directives/skipAuth/skipAuth.test.ts', + '/api/src/directives/skipAuth/skipAuth.ts', + '/api/src/functions/graphql.ts', + '/api/src/graphql/.keep', + '/api/src/lib/auth.ts', + '/api/src/lib/db.ts', + '/api/src/lib/logger.ts', + '/api/src/services/.keep', + '/api/tsconfig.json', + '/gitignore.template', + '/graphql.config.js', + '/jest.config.js', + '/package.json', + '/prettier.config.js', + '/redwood.toml', + '/scripts/.keep', + '/scripts/seed.ts', + '/scripts/tsconfig.json', + '/web/jest.config.js', + '/web/package.json', + '/web/public/README.md', + '/web/public/favicon.png', + '/web/public/robots.txt', + '/web/src/App.tsx', + '/web/src/Routes.tsx', + '/web/src/components/.keep', + '/web/src/index.css', + '/web/src/index.html', + '/web/src/layouts/.keep', + '/web/src/pages/FatalErrorPage/FatalErrorPage.tsx', + '/web/src/pages/NotFoundPage/NotFoundPage.tsx', + '/web/tsconfig.json', + ]) +}) + +/** + * Get all the files in a directory. + * + * @param {string} dir + * @returns string[] + */ +function walk(dir) { + /** @type {string[]} */ + let files = [] + + const fileList = fs.readdirSync(dir) + + fileList.forEach((file) => { + file = path.join(dir, file) + + const stat = fs.statSync(file) + + if (!stat) { + throw new Error(`Failed to get stats for ${file}`) + } + + if (stat.isDirectory()) { + // Recurse into directory + files = files.concat(walk(file)) + } else { + // It's a file + files.push(file) + } + }) + + return files.map((file) => file.replace(TEMPLATE_PATH, '')) +}