diff --git a/.gitignore b/.gitignore index b30639b1..b2636ae4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -# For this repo, let's not all share our convex projects -convex.json +# Login info for npm publishing +.npmrc # Or our local env .env.local diff --git a/convex/example.test.ts b/convex/example.test.ts index 2d63fa29..8194ee9d 100644 --- a/convex/example.test.ts +++ b/convex/example.test.ts @@ -1,8 +1,7 @@ import { api } from "./_generated/api"; import { ConvexTestingHelper } from "convex-helpers/testing"; -// Un-skip this test to run end-to-end tests. Also see vitest.config.mts. -describe.skip("testingExample", () => { +describe("testingExample", () => { let t: ConvexTestingHelper; beforeEach(() => { diff --git a/convex/vitest.config.mts b/convex/vitest.config.mts new file mode 100644 index 00000000..9c66d704 --- /dev/null +++ b/convex/vitest.config.mts @@ -0,0 +1,16 @@ +import { defineConfig } from "vitest/config"; + +// https://vitejs.dev/config/ +export default defineConfig({ + test: { + environment: "edge-runtime", + exclude: [], + passWithNoTests: true, + + // Only run one suite at a time because all of our tests are running against + // the same backend and we don't want to leak state. + maxWorkers: 1, + minWorkers: 1, + globals: true, + }, +}); diff --git a/delete-entrypoints.mjs b/delete-entrypoints.mjs new file mode 100644 index 00000000..e20163ef --- /dev/null +++ b/delete-entrypoints.mjs @@ -0,0 +1,26 @@ +import fs from "node:fs"; +import path from "node:path"; +import { + __dirname, + entryPointFiles, + entryPointFromFile, +} from "./generate-utils.mjs"; + +function createEntrypoints() { + for (const entryPointFile of entryPointFiles()) { + const entryPoint = entryPointFromFile(entryPointFile); + if (entryPoint === ".") continue; + + const entryPointPath = path.join(__dirname, entryPoint); + const packagePath = path.join(entryPointPath, "package.json"); + if (fs.existsSync(packagePath)) { + fs.rmSync(packagePath); + } + const fileName = path.parse(entryPointFile).name; + if (fileName !== "index" && fs.existsSync(entryPointPath)) { + fs.rmdirSync(entryPointPath); + } + } +} + +createEntrypoints(); diff --git a/generate-entrypoints.mjs b/generate-entrypoints.mjs new file mode 100644 index 00000000..fb6928c5 --- /dev/null +++ b/generate-entrypoints.mjs @@ -0,0 +1,46 @@ +import fs from "node:fs"; +import path from "node:path"; +import { + __dirname, + entryPointFiles, + entryPointFromFile, +} from "./generate-utils.mjs"; + +function createEntrypoints() { + for (const entryPointFile of entryPointFiles()) { + const entryPoint = entryPointFromFile(entryPointFile); + if (entryPoint === ".") continue; + const entryPointPath = path.join(__dirname, entryPoint); + if (!fs.existsSync(entryPointPath)) { + // make directory + fs.mkdirSync(entryPointPath, { recursive: true }); + } + const extensionless = path.join( + path.parse(entryPointFile).dir, + path.parse(entryPointFile).name, + ); + const packagePath = path.join(entryPointPath, "package.json"); + const parts = entryPoint.split("/"); + const dist = path.join( + parts + .slice(1) + .map(() => "..") + .join("/"), + "dist", + ); + fs.writeFileSync( + packagePath, + JSON.stringify( + { + type: "module", + module: path.join(dist, `${extensionless}.js`), + types: path.join(dist, `${extensionless}.d.ts`), + }, + null, + 2, + ), + ); + } +} + +createEntrypoints(); diff --git a/generate-exports.mjs b/generate-exports.mjs new file mode 100644 index 00000000..9fecc94c --- /dev/null +++ b/generate-exports.mjs @@ -0,0 +1,46 @@ +import fs from "node:fs"; +import path from "node:path"; +import process from "node:process"; +import { + __dirname, + entryPointFiles, + entryPointFromFile, +} from "./generate-utils.mjs"; + +function generateExport(source) { + let extensionless = path.join( + path.parse(source).dir, + path.parse(source).name, + ); + + return { + types: `./dist/${extensionless}.d.ts`, + default: `./dist/${extensionless}.js`, + }; +} + +function generateExports() { + const obj = {}; + for (const entryPoint of entryPointFiles()) { + obj[entryPointFromFile(entryPoint)] = generateExport(entryPoint); + } + return obj; +} + +function generatePackageExports() { + const packageJson = JSON.parse( + fs.readFileSync(path.join(__dirname, "package.json")), + ); + const actual = packageJson.exports; + const expected = generateExports(); + if (JSON.stringify(actual) !== JSON.stringify(expected)) { + packageJson.exports = expected; + fs.writeFileSync( + path.join(__dirname, "package.json"), + JSON.stringify(packageJson, null, 2) + "\n", + ); + process.exit(1); + } +} + +generatePackageExports(); diff --git a/generate-utils.mjs b/generate-utils.mjs new file mode 100644 index 00000000..ae3871f3 --- /dev/null +++ b/generate-utils.mjs @@ -0,0 +1,42 @@ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +export const __dirname = path.join( + path.dirname(fileURLToPath(new URL(import.meta.url))), + "packages", + "convex-helpers", +); + +function directoryContents(dirname) { + return fs + .readdirSync(path.join(__dirname, dirname), { recursive: true }) + .filter((filename) => filename.endsWith(".ts") || filename.endsWith(".tsx")) + .filter((filename) => !filename.includes(".test")) + .filter((filename) => !filename.includes("_generated")) + .map((filename) => path.join(dirname, filename)); +} + +export function entryPointFiles() { + return [ + "./index.ts", + "./testing.ts", + "./validators.ts", + ...directoryContents("react"), + ...directoryContents("server"), + ]; +} + +export function entryPointFromFile(source) { + let entryPoint = path.join(path.parse(source).dir, path.parse(source).name); + + if (path.parse(source).name === "index") { + entryPoint = path.parse(source).dir; + } + + if (!entryPoint.startsWith(".")) { + entryPoint = `./${entryPoint}`; + } + + return entryPoint; +} diff --git a/package-lock.json b/package-lock.json index cab778a5..e671d2f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "dependencies": { "classnames": "^2.3.2", "convex": "^1.13.0", - "convex-helpers": "file:packages/convex-helpers/dist", + "convex-helpers": "file:packages/convex-helpers", "hono": "^4.3.6", "react": "^18.0.0", "react-dom": "^18.0.0", @@ -2553,7 +2553,7 @@ } }, "node_modules/convex-helpers": { - "resolved": "packages/convex-helpers/dist", + "resolved": "packages/convex-helpers", "link": true }, "node_modules/convex-test": { @@ -6436,9 +6436,8 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "packages/convex-helpers/dist": { - "name": "convex-helpers", - "version": "0.1.49-alpha.0", + "packages/convex-helpers": { + "version": "0.1.49-alpha.2", "license": "MIT", "peerDependencies": { "convex": "^1.13.0", diff --git a/package.json b/package.json index c5c00c11..07adb83b 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "dev:helpers": "cd packages/convex-helpers && chokidar '*.ts' 'server/**/*.ts' 'react/**/*.ts*' 'tsconfig*.json' 'package.json' -c 'npm run build' --initial", "predev": "cd packages/convex-helpers && npm run build", "lint": "tsc --project tsconfig.test.json", - "build": "cd packages/convex-helpers && node generate-exports.mjs && mkdir -p dist && cp -r *.ts server react ./package.json ./tsconfig.json ./README.md ../../LICENSE ./.npmignore dist/ && rm dist/server/_generated/_ignore.ts && cd dist/ && tsc", + "build": "node generate-exports.mjs && cd packages/convex-helpers && tsc", "clean": "rm -rf packages/convex-helpers/dist", "test": "vitest run", "test:watch": "vitest", @@ -18,12 +18,13 @@ "test:coverage": "vitest run --coverage --coverage.reporter=text", "testFunctionsExistingBackend": "just convex deploy && just convex env set IS_TEST true && vitest --run convex/example.test.ts", "testFunctions": "node backendHarness.js 'npm run testFunctionsExistingBackend'", - "arethetypeswrong": "cd packges/convex-helpers/dist && attw $(npm pack)" + "arethetypeswrong": "cd packages/convex-helpers && attw $(npm pack)", + "publish": "./publish.sh" }, "dependencies": { "classnames": "^2.3.2", "convex": "^1.13.0", - "convex-helpers": "file:packages/convex-helpers/dist", + "convex-helpers": "file:packages/convex-helpers", "hono": "^4.3.6", "react": "^18.0.0", "react-dom": "^18.0.0", diff --git a/packages/convex-helpers/.npmignore b/packages/convex-helpers/.npmignore index eaa8a157..c00f0192 100644 --- a/packages/convex-helpers/.npmignore +++ b/packages/convex-helpers/.npmignore @@ -1,4 +1,3 @@ -node_modules -package-lock.json +vitest.config.mts *.tgz *.tsbuildinfo diff --git a/packages/convex-helpers/generate-exports.mjs b/packages/convex-helpers/generate-exports.mjs deleted file mode 100644 index ab247099..00000000 --- a/packages/convex-helpers/generate-exports.mjs +++ /dev/null @@ -1,89 +0,0 @@ -import fs from "node:fs"; -import path from "node:path"; -import process from "node:process"; -import { fileURLToPath } from "node:url"; - -const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url))); - -function directoryContents(dirname) { - return fs - .readdirSync(path.join(__dirname, dirname)) - .filter((filename) => filename.endsWith(".ts") || filename.endsWith(".tsx")) - .filter((filename) => !filename.includes(".test")) - .map((filename) => path.join(dirname, filename)); -} - -function entryPointFiles() { - return [ - "./index.ts", - "./testing.ts", - "./validators.ts", - ...directoryContents("react"), - ...directoryContents("react/cache"), - ...directoryContents("server"), - ]; -} - -function indent(s, n) { - const lines = s.split("\n"); - return ( - lines.shift() + "\n" + lines.map((line) => " ".repeat(n) + line).join("\n") - ); -} - -function entryPointFromFile(source) { - let entryPoint = path.join(path.parse(source).dir, path.parse(source).name); - - if (path.parse(source).name === "index") { - entryPoint = path.parse(source).dir; - } - - if (!entryPoint.startsWith(".")) { - entryPoint = `./${entryPoint}`; - } - - return entryPoint; -} - -function generateExport(source) { - let extensionless = path.join( - path.parse(source).dir, - path.parse(source).name, - ); - - return { - types: `./${extensionless}.d.ts`, - default: `./${extensionless}.js`, - }; -} - -function generateExports() { - const obj = {}; - for (const entryPoint of entryPointFiles()) { - obj[entryPointFromFile(entryPoint)] = generateExport(entryPoint); - } - return obj; -} - -function checkPackageJsonExports() { - const packageJson = JSON.parse( - fs.readFileSync(path.join(__dirname, "package.json")), - ); - const actual = packageJson.exports; - const expected = generateExports(); - if (JSON.stringify(actual) !== JSON.stringify(expected)) { - console.error("-------------------->8--------------------"); - console.log( - ` "exports": ${indent(JSON.stringify(expected, null, 2), 2)},`, - ); - console.error("-------------------->8--------------------"); - console.error( - "`package.json` exports are not correct. Copy exports from above or run", - ); - console.error("node generate-exports.mjs | pbcopy"); - console.error("and paste into package.json."); - process.exit(1); - } -} - -checkPackageJsonExports(); diff --git a/packages/convex-helpers/package.json b/packages/convex-helpers/package.json index 77494a78..1657a04a 100644 --- a/packages/convex-helpers/package.json +++ b/packages/convex-helpers/package.json @@ -1,86 +1,88 @@ { "name": "convex-helpers", - "version": "0.1.49-alpha.0", + "version": "0.1.49-alpha.2", "description": "A collection of useful code to complement the official convex package.", "type": "module", "exports": { ".": { - "types": "./index.d.ts", - "default": "./index.js" + "types": "./dist/index.d.ts", + "default": "./dist/index.js" }, "./testing": { - "types": "./testing.d.ts", - "default": "./testing.js" + "types": "./dist/testing.d.ts", + "default": "./dist/testing.js" }, "./validators": { - "types": "./validators.d.ts", - "default": "./validators.js" + "types": "./dist/validators.d.ts", + "default": "./dist/validators.js" }, "./react": { - "types": "./react/index.d.ts", - "default": "./react/index.js" + "types": "./dist/react/index.d.ts", + "default": "./dist/react/index.js" }, "./react/sessions": { - "types": "./react/sessions.d.ts", - "default": "./react/sessions.js" + "types": "./dist/react/sessions.d.ts", + "default": "./dist/react/sessions.js" }, "./react/cache/hooks": { - "types": "./react/cache/hooks.d.ts", - "default": "./react/cache/hooks.js" + "types": "./dist/react/cache/hooks.d.ts", + "default": "./dist/react/cache/hooks.js" }, "./react/cache/provider": { - "types": "./react/cache/provider.d.ts", - "default": "./react/cache/provider.js" + "types": "./dist/react/cache/provider.d.ts", + "default": "./dist/react/cache/provider.js" }, "./server/customFunctions": { - "types": "./server/customFunctions.d.ts", - "default": "./server/customFunctions.js" + "types": "./dist/server/customFunctions.d.ts", + "default": "./dist/server/customFunctions.js" }, "./server/filter": { - "types": "./server/filter.d.ts", - "default": "./server/filter.js" + "types": "./dist/server/filter.d.ts", + "default": "./dist/server/filter.js" }, "./server/hono": { - "types": "./server/hono.d.ts", - "default": "./server/hono.js" + "types": "./dist/server/hono.d.ts", + "default": "./dist/server/hono.js" }, "./server": { - "types": "./server/index.d.ts", - "default": "./server/index.js" + "types": "./dist/server/index.d.ts", + "default": "./dist/server/index.js" }, "./server/migrations": { - "types": "./server/migrations.d.ts", - "default": "./server/migrations.js" + "types": "./dist/server/migrations.d.ts", + "default": "./dist/server/migrations.js" }, "./server/pagination": { - "types": "./server/pagination.d.ts", - "default": "./server/pagination.js" + "types": "./dist/server/pagination.d.ts", + "default": "./dist/server/pagination.js" }, "./server/rateLimit": { - "types": "./server/rateLimit.d.ts", - "default": "./server/rateLimit.js" + "types": "./dist/server/rateLimit.d.ts", + "default": "./dist/server/rateLimit.js" }, "./server/relationships": { - "types": "./server/relationships.d.ts", - "default": "./server/relationships.js" + "types": "./dist/server/relationships.d.ts", + "default": "./dist/server/relationships.js" }, "./server/retries": { - "types": "./server/retries.d.ts", - "default": "./server/retries.js" + "types": "./dist/server/retries.d.ts", + "default": "./dist/server/retries.js" }, "./server/rowLevelSecurity": { - "types": "./server/rowLevelSecurity.d.ts", - "default": "./server/rowLevelSecurity.js" + "types": "./dist/server/rowLevelSecurity.d.ts", + "default": "./dist/server/rowLevelSecurity.js" }, "./server/sessions": { - "types": "./server/sessions.d.ts", - "default": "./server/sessions.js" + "types": "./dist/server/sessions.d.ts", + "default": "./dist/server/sessions.js" }, "./server/zod": { - "types": "./server/zod.d.ts", - "default": "./server/zod.js" + "types": "./dist/server/zod.d.ts", + "default": "./dist/server/zod.js" } }, + "module": "./dist/index.js", + "types": "./dist/index.d.ts", "repository": { "type": "git", "url": "git+https://github.com/get-convex/convex-helpers.git", diff --git a/packages/convex-helpers/tsconfig.json b/packages/convex-helpers/tsconfig.json index 3fa29f3d..eecd7ab1 100644 --- a/packages/convex-helpers/tsconfig.json +++ b/packages/convex-helpers/tsconfig.json @@ -55,7 +55,7 @@ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./", /* Specify an output folder for all emitted files. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ diff --git a/publish.sh b/publish.sh index 99306afe..90555401 100755 --- a/publish.sh +++ b/publish.sh @@ -34,6 +34,13 @@ npm view convex-helpers@alpha version read -r -p "Enter the new version number (hit enter for $current): " version +node generate-entrypoints.mjs +function cleanup() { + node delete-entrypoints.mjs + git co package-lock.json packages/convex-helpers/package.json +} +trap cleanup EXIT + pushd packages/convex-helpers >/dev/null if [ -n "$version" ]; then npm pkg set version="$version" @@ -41,30 +48,23 @@ else version=$current fi -cp package.json dist/ - -cd dist npm publish --dry-run popd >/dev/null echo "^^^ DRY RUN ^^^" read -r -p "Publish $version to npm? (y/n): " publish if [ "$publish" = "y" ]; then npm i - git add package.json package-lock.json packages/convex-helpers/package.json - # If there's nothing to commit, continue - git commit -m "npm $version" || true - - pushd packages/convex-helpers/dist >/dev/null + pushd packages/convex-helpers >/dev/null if (echo "$version" | grep alpha >/dev/null); then npm publish --tag alpha else npm publish fi popd >/dev/null + git add package.json package-lock.json packages/convex-helpers/package.json + # If there's nothing to commit, continue + git commit -m "npm $version" || true git tag "npm/$version" git push origin "npm/$version" git push -else - echo "Aborted." - git co package-lock.json packages/convex-helpers/package.json fi diff --git a/vite.config.mts b/vite.config.mts index dc79b3c0..b071ef3e 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -6,14 +6,8 @@ export default defineConfig({ plugins: [react()], test: { environment: "jsdom", - exclude: ["**/node_modules/**", "packages/**/dist/**"], - passWithNoTests: true, + exclude: ["node_modules/**", "convex/**", "packages/**"], - // For the end-to-end tests, uncomment these lines - // Only run one suite at a time because all of our tests are running against - // the same backend and we don't want to leak state. - // maxWorkers: 1, - // minWorkers: 1, globals: true, }, }); diff --git a/vitest.workspace.ts b/vitest.workspace.ts new file mode 100644 index 00000000..7e879254 --- /dev/null +++ b/vitest.workspace.ts @@ -0,0 +1 @@ +export default [".", "packages/convex-helpers"];