diff --git a/.vscode/settings.json b/.vscode/settings.json index 9466963..db00b8e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "files.associations": { "wrangler.json": "jsonc" - } + }, + "typescript.experimental.useTsgo": true } diff --git a/apps/http-helmet/remix-classic-compiler/remix.config.js b/apps/http-helmet/remix-classic-compiler/remix.config.js index 0ec9ed3..d91eb2b 100644 --- a/apps/http-helmet/remix-classic-compiler/remix.config.js +++ b/apps/http-helmet/remix-classic-compiler/remix.config.js @@ -1,4 +1,4 @@ -/** @type {import('@remix-run/dev').AppConfig} */ +/** @type {import("@remix-run/dev").AppConfig} */ export default { ignoredRouteFiles: ["**/.*"], // appDirectory: "app", diff --git a/apps/http-helmet/remix-vite/app/entry.client.tsx b/apps/http-helmet/remix-vite/app/entry.client.tsx index 6129132..f5c4e8a 100644 --- a/apps/http-helmet/remix-vite/app/entry.client.tsx +++ b/apps/http-helmet/remix-vite/app/entry.client.tsx @@ -1,7 +1,8 @@ /** - * By default, Remix will handle hydrating your app on the client for you. - * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ - * For more information, see https://remix.run/file-conventions/entry.client + * By default, Remix will handle hydrating your app on the client for you. You + * are free to delete this file if you'd like to, but if you ever want it + * revealed again, you can run `npx remix reveal` ✨ For more information, see + * https://remix.run/file-conventions/entry.client */ import { RemixBrowser } from "@remix-run/react"; diff --git a/apps/http-helmet/remix-vite/app/entry.server.tsx b/apps/http-helmet/remix-vite/app/entry.server.tsx index c28a664..ff70002 100644 --- a/apps/http-helmet/remix-vite/app/entry.server.tsx +++ b/apps/http-helmet/remix-vite/app/entry.server.tsx @@ -1,7 +1,8 @@ /** - * By default, Remix will handle generating the HTTP Response for you. - * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ - * For more information, see https://remix.run/file-conventions/entry.server + * By default, Remix will handle generating the HTTP Response for you. You are + * free to delete this file if you'd like to, but if you ever want it revealed + * again, you can run `npx remix reveal` ✨ For more information, see + * https://remix.run/file-conventions/entry.server */ import { diff --git a/package.json b/package.json index 10c1e41..d6c1e30 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "license": "MIT", "type": "module", "scripts": { - "build:packages": "pnpm run --recursive --parallel --filter \"./packages/**\" build", - "build:apps": "pnpm run --recursive --parallel --filter \"./apps/**\" build", + "build:packages": "pnpm run --recursive --parallel --sort --filter \"./packages/**\" build", + "build:apps": "pnpm run --recursive --parallel --sort --filter \"./apps/**\" build", "build": "pnpm run --sequential \"/^build:.*/\"", "changeset": "changeset", "changeset:release": "pnpm run build && changeset publish", @@ -16,7 +16,6 @@ "format": "prettier --cache --ignore-path .gitignore --ignore-path .prettierignore --write .", "lint": "pnpm run --recursive --parallel lint", "prepublishOnly": "pnpm run build", - "publish": "./scripts/publish.js", "test": "vitest", "test:ci": "vitest --coverage", "typecheck": "pnpm run --recursive typecheck", @@ -27,17 +26,20 @@ "@changesets/cli": "^2.29.5", "@manypkg/get-packages": "^3.0.0", "@types/node": "catalog:", + "@typescript/native-preview": "catalog:", "@vitest/coverage-v8": "catalog:", "chalk": "^5.4.1", "jsonfile": "^6.1.0", "junit": "^1.4.9", "pkg-pr-new": "^0.0.54", "prettier": "^3.6.2", + "prettier-plugin-jsdoc": "^1.3.3", "prettier-plugin-organize-imports": "^4.1.0", "prettier-plugin-sort-package-json": "^1.1.0", "prompt-confirm": "^2.0.4", "semver": "^7.7.2", "typedoc": "^0.28.7", + "typescript": "catalog:", "vitest": "catalog:", "wrangler": "^4.25.0" }, diff --git a/packages/config/package.json b/packages/config/package.json new file mode 100644 index 0000000..2486b32 --- /dev/null +++ b/packages/config/package.json @@ -0,0 +1,56 @@ +{ + "name": "@mcansh/config", + "version": "0.1.0", + "description": "shared configuration for packages in the mcansh/packages monorepo", + "repository": { + "type": "git", + "url": "git+https://github.com/mcansh/packages.git", + "directory": "./packages/config" + }, + "author": "Logan McAnsh (https://mcan.sh/)", + "license": "MIT", + "type": "module", + "scripts": { + "build": "tsdown", + "dev": "tsdown --watch", + "format": "prettier --ignore-path .gitignore --ignore-path .prettierignore --ignore-unknown --write .", + "prepublishOnly": "node --run build", + "test": "vitest", + "typecheck": "tsgo" + }, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": "./dist/index.js", + "./package.json": "./package.json" + }, + "files": [ + "dist", + "package.json", + "README.md", + "LICENSE" + ], + "engines": { + "node": ">=20" + }, + "dependencies": { + "ts-deepmerge": "^7.0.3" + }, + "peerDependencies": { + "tsdown": "^0.12.0" + }, + "devDependencies": { + "@arethetypeswrong/core": "catalog:", + "@total-typescript/tsconfig": "catalog:", + "@types/node": "catalog:", + "@typescript/native-preview": "catalog:", + "@vitest/coverage-v8": "catalog:", + "tsdown": "catalog:", + "typescript": "catalog:", + "vitest": "^3.2.4" + }, + "imports": { + "#*": "./*" + } +} diff --git a/packages/config/src/build.ts b/packages/config/src/build.ts new file mode 100644 index 0000000..bda804d --- /dev/null +++ b/packages/config/src/build.ts @@ -0,0 +1,80 @@ +import { merge } from "ts-deepmerge"; +import type { UserConfig, UserConfigFn } from "tsdown"; + +export const defaultBuildConfig = { + dts: true, + format: "esm", + tsconfig: "./tsconfig.json", + sourcemap: true, + exports: true, + clean: true, + publint: true, + attw: { profile: "node16" }, + skipNodeModulesBundle: true, + platform: "neutral", + define: { + "import.meta.vitest": "undefined", + }, +} satisfies UserConfig; + +export function mergeBuildConfig( + userConfig?: UserConfig | UserConfigFn, +): UserConfig { + if (typeof userConfig === "undefined") return defaultBuildConfig; + let config = + typeof userConfig === "function" + ? userConfig(defaultBuildConfig) + : userConfig; + return merge(defaultBuildConfig, config); +} + +if (import.meta.vitest) { + let { describe, expect, it } = import.meta.vitest; + + describe("mergeBuildConfig", () => { + it("returns default config when no input is provided", () => { + let config = mergeBuildConfig(); + expect(config).toEqual(defaultBuildConfig); + }); + + it("returns default config when empty object is provided", () => { + let config = mergeBuildConfig({}); + expect(config).toEqual(defaultBuildConfig); + }); + + it("returns default config when function returns empty object", () => { + let config = mergeBuildConfig(() => ({})); + expect(config).toEqual(defaultBuildConfig); + }); + + it("merges user config when using function", () => { + let config = mergeBuildConfig((config) => { + config.dts = false; + return {}; + }); + + expect(config).toEqual({ + ...defaultBuildConfig, + dts: false, + }); + }); + + it.each([ + { dts: false }, + { format: "cjs" }, + { sourcemap: false }, + { format: ["cjs", "esm"] }, + { define: { __DEV__: "true" } }, + ] satisfies UserConfig[])("merges configs", (input) => { + let config = mergeBuildConfig(input); + expect(config).toEqual({ + ...defaultBuildConfig, + ...input, + define: { + ...defaultBuildConfig.define, + ...input.define, + }, + }); + }); + }); +} diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts new file mode 100644 index 0000000..a454f00 --- /dev/null +++ b/packages/config/src/index.ts @@ -0,0 +1 @@ +export { defaultBuildConfig, mergeBuildConfig } from "./build"; diff --git a/packages/config/tsconfig.json b/packages/config/tsconfig.json new file mode 100644 index 0000000..13d079c --- /dev/null +++ b/packages/config/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@total-typescript/tsconfig/bundler/no-dom", + "include": ["./src/**/*", "./vitest.setup.ts"], + "compilerOptions": { + "allowImportingTsExtensions": true, + "moduleResolution": "Bundler", + "forceConsistentCasingInFileNames": true, + "types": ["vitest/importMeta"] + } +} diff --git a/packages/config/tsdown.config.ts b/packages/config/tsdown.config.ts new file mode 100644 index 0000000..221fa74 --- /dev/null +++ b/packages/config/tsdown.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "tsdown"; +import { defaultBuildConfig } from "./src/build.ts"; + +export default defineConfig({ + ...defaultBuildConfig, + entry: { index: "./src/index.ts" }, +}); diff --git a/packages/config/typedoc.json b/packages/config/typedoc.json new file mode 100644 index 0000000..03b2c32 --- /dev/null +++ b/packages/config/typedoc.json @@ -0,0 +1,3 @@ +{ + "entryPoints": ["./src/index.ts", "./src/build.ts"] +} diff --git a/packages/config/vitest.config.ts b/packages/config/vitest.config.ts new file mode 100644 index 0000000..1248a14 --- /dev/null +++ b/packages/config/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineProject } from "vitest/config"; + +export default defineProject({ + test: { + includeSource: ["./src/**/*.{js,ts}"], + name: "config", + }, +}); diff --git a/packages/http-helmet/package.json b/packages/http-helmet/package.json index 6be5e2d..f84b164 100644 --- a/packages/http-helmet/package.json +++ b/packages/http-helmet/package.json @@ -25,7 +25,7 @@ "dev": "tsdown --watch", "prepublishOnly": "npm run build", "test": "vitest --project http-helmet", - "typecheck": "tsc" + "typecheck": "tsgo" }, "main": "./dist/index.cjs", "module": "./dist/index.js", @@ -64,6 +64,7 @@ }, "devDependencies": { "@arethetypeswrong/core": "catalog:", + "@mcansh/config": "workspace:", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.3.0", @@ -71,6 +72,7 @@ "@types/node": "catalog:", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", + "@typescript/native-preview": "catalog:", "@vitest/coverage-v8": "catalog:", "content-security-policy-parser": "^0.6.0", "happy-dom": "^18.0.1", diff --git a/packages/http-helmet/src/helmet.ts b/packages/http-helmet/src/helmet.ts index 3533374..90eabdf 100644 --- a/packages/http-helmet/src/helmet.ts +++ b/packages/http-helmet/src/helmet.ts @@ -33,55 +33,91 @@ export type XSSProtection = "0" | "1" | "1; mode=block" | `1; report=${string}`; type BaseSecureHeaders = { /** - * The X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a ``, `