Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ jobs:
smoke:
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
react: [18, 19]
defaults:
run:
working-directory: ./package
Expand All @@ -64,10 +68,11 @@ jobs:
- run: npm run smoke
env:
CI: 'true'
REACT_VERSION: ${{ matrix.react }}

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: ${{ !cancelled() }}
with:
name: playwright-report-smoke
name: playwright-report-smoke-react${{ matrix.react }}
path: package/playwright-report-smoke/
retention-days: 14
7 changes: 6 additions & 1 deletion .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ jobs:
smoke:
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
react: [18, 19]
defaults:
run:
working-directory: ./package
Expand All @@ -66,11 +70,12 @@ jobs:
- run: npm run smoke
env:
CI: 'true'
REACT_VERSION: ${{ matrix.react }}

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: ${{ !cancelled() }}
with:
name: release-playwright-report-smoke
name: release-playwright-report-smoke-react${{ matrix.react }}
path: package/playwright-report-smoke/
retention-days: 14

Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ $ npm i react-qrcode-reader

| react-qrcode-reader version | React version |
| --------------------------- | ------------- |
| 3.x.x | 19.x |
| 3.1.x | 19.x, 18.x |
| 3.0.x | 19.x |
| 2.x.x | 18.x, 17.x |

Please ensure your React version matches the supported version of the package.

Since 3.1.0 the package no longer ships `@types/react` as a runtime dependency, so its
types resolve against the `@types/react` in your own project.

# how to use

## 1. with onRead
Expand Down
24 changes: 15 additions & 9 deletions package/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions package/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-qrcode-reader",
"version": "3.0.3",
"version": "3.1.0",
"author": {
"name": "ysuzuki19",
"url": "https://ysuzuki19.github.io"
Expand Down Expand Up @@ -43,16 +43,12 @@
"prepublishOnly": "cp ../README.md . && cp ../LICENSE . && cp ../MIGRATION.md . && npm run build"
},
"peerDependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0"
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"dependencies": {
"@types/node": "^24.5.2",
"@types/react": "^19.1.6",
"@types/react-dom": "^19.1.6",
"jsqr": "^1.4.0",
"react-webcam": "^7.0.1",
"typescript": "^5.4.5"
"react-webcam": "^7.0.1"
},
"devDependencies": {
"@biomejs/biome": "^2.4.15",
Expand All @@ -63,6 +59,9 @@
"@rollup/plugin-terser": "^1.0.0",
"@rollup/plugin-typescript": "^12.1.4",
"@types/jest": "^30.0.0",
"@types/node": "^24.5.2",
"@types/react": "^19.1.6",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^5.2.0",
"jest": "^30.1.3",
"jest-environment-jsdom": "^30.1.2",
Expand All @@ -74,6 +73,7 @@
"ts-jest": "^29.0.5",
"ts-node": "^10.9.2",
"tslib": "^2.8.1",
"typescript": "^5.4.5",
"vite": "^8.0.16"
},
"browserslist": {
Expand Down
14 changes: 11 additions & 3 deletions package/rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,23 @@ const dts_config = {
const plugins_for_build = [
resolve(),
commonjs(),
typescript({ compilerOptions: { outDir: 'dist' } }),
// The classic runtime keeps the emitted code's only React dependency on
// `react` itself. `react-jsx` would emit `react/jsx-runtime`, which has no
// UMD global and would therefore have to be inlined into the UMD bundle --
// pinning the published artifact to one React major.
typescript({ compilerOptions: { outDir: 'dist', jsx: 'react' } }),
babel({
babelHelpers: 'bundled',
extensions: ['.ts'],
exclude: 'node_modules/**',
}),
];

const external = ['react', 'react-dom'];
const external = (id) =>
id === 'react' ||
id.startsWith('react/') ||
id === 'react-dom' ||
id.startsWith('react-dom/');

const es_config = {
input: entry,
Expand All @@ -48,7 +56,7 @@ const umd_config = {
exports: 'named',
indent: false,
globals: {
react: 'react',
react: 'React',
},
},
external,
Expand Down
66 changes: 57 additions & 9 deletions package/scripts/smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@
// Build → pack → install the tarball into smoke/ → run the smoke Playwright
// suite. Mirrors what `npm publish` would produce so that the test exercises
// the actual distributed artifact, not the source tree.
//
// REACT_VERSION selects which React major the consumer app runs on; it must be
// one of the majors declared in the package's peerDependencies.

import { spawnSync } from 'node:child_process';
import { copyFileSync, existsSync, readdirSync, rmSync, unlinkSync } from 'node:fs';
import {
copyFileSync,
existsSync,
readFileSync,
readdirSync,
rmSync,
unlinkSync,
} from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

Expand All @@ -13,6 +23,28 @@ const packageDir = resolve(here, '..');
const repoRoot = resolve(packageDir, '..');
const smokeDir = resolve(packageDir, 'smoke');

// smoke/package-lock.json pins the default major, so that leg is fully
// deterministic. Other majors are overridden with --no-save, which keeps the
// committed lockfile authoritative instead of forking it per major.
const DEFAULT_REACT_MAJOR = '19';
const REACT_OVERLAYS = {
18: [
'react@^18.3.1',
'react-dom@^18.3.1',
'@types/react@^18.3.12',
'@types/react-dom@^18.3.1',
],
};

const reactMajor = process.env.REACT_VERSION ?? DEFAULT_REACT_MAJOR;
if (reactMajor !== DEFAULT_REACT_MAJOR && !REACT_OVERLAYS[reactMajor]) {
console.error(
`REACT_VERSION=${reactMajor} is not supported; expected one of ` +
`${[DEFAULT_REACT_MAJOR, ...Object.keys(REACT_OVERLAYS)].join(', ')}`,
);
process.exit(1);
}

function run(cmd, args, opts = {}) {
const res = spawnSync(cmd, args, { stdio: 'inherit', ...opts });
if (res.status !== 0) {
Expand Down Expand Up @@ -58,15 +90,20 @@ const tgzPath = resolve(packageDir, tgz);
console.log(`packed: ${tgz}`);

// 5. Re-install smoke/ base deps from the committed lockfile (deterministic),
// then overlay the freshly packed tarball. `--no-save` keeps both
// package.json and package-lock.json untouched, so the working tree stays
// clean across runs.
// then overlay the freshly packed tarball (and the React override, if any).
// `--no-save` keeps both package.json and package-lock.json untouched, so the
// working tree stays clean across runs.
step('install smoke/ base deps (npm ci)');
rmSync(resolve(smokeDir, 'node_modules'), { recursive: true, force: true });
run('npm', ['ci', '--no-audit', '--no-fund'], { cwd: smokeDir });

step(`overlay tarball: ${tgz}`);
run('npm', ['install', '--no-save', '--no-audit', '--no-fund', tgzPath], {
// The tarball and the React override must go in as ONE install: npm reconciles
// the whole tree against smoke/package.json on every install, so a React
// override applied in a separate, earlier step would be reverted to the pinned
// default by the tarball install.
const overlay = [tgzPath, ...(REACT_OVERLAYS[reactMajor] ?? [])];
step(`overlay tarball (${tgz}) + React ${reactMajor}`);
run('npm', ['install', '--no-save', '--no-audit', '--no-fund', ...overlay], {
cwd: smokeDir,
});

Expand All @@ -77,9 +114,20 @@ if (!existsSync(installed)) {
process.exit(1);
}

// Sanity: the consumer app must actually be on the requested major, otherwise
// a silently-reverted override would let the matrix report a false pass.
const reactPkg = resolve(smokeDir, 'node_modules/react/package.json');
const installedReact = JSON.parse(readFileSync(reactPkg, 'utf8')).version;
if (installedReact.split('.')[0] !== reactMajor) {
console.error(`expected React ${reactMajor}.x in smoke/, got ${installedReact}`);
process.exit(1);
}
console.log(`smoke/ react: ${installedReact}`);

// 6. Type-check the consumer fixture against the built artifact's .d.ts.
// This must run AFTER the overlay so @types/react (transitive via the
// tarball's `dependencies`) and dist/index.d.ts are present.
// This must run AFTER the overlay so dist/index.d.ts is present. It resolves
// `import React from 'react'` inside the .d.ts against smoke/'s own
// @types/react, which is what pins the types to the React major under test.
step('typecheck smoke/ against dist/index.d.ts');
run('npm', ['run', 'typecheck'], { cwd: smokeDir });

Expand All @@ -93,4 +141,4 @@ run('npx', ['playwright', 'test', '--config=playwright.smoke.config.ts'], {
cwd: packageDir,
});

console.log('\nsmoke: all checks passed');
console.log(`\nsmoke: all checks passed (React ${installedReact})`);
Loading
Loading