Skip to content

Commit

Permalink
qual: pin latest metadata-parser
Browse files Browse the repository at this point in the history
  • Loading branch information
lourot committed Apr 23, 2024
1 parent 36a60a9 commit 8131ae8
Show file tree
Hide file tree
Showing 9 changed files with 1,224 additions and 1,759 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
- name: Upload component
uses: actions/upload-artifact@v3
with:
name: image-display-control-metadata-parser
name: react-image-display-control
path: frameright-react-image-display-control-*.tgz
if-no-files-found: error

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ An easy way to do [Image Display Control](https://frameright.io) in your React
web app. Made with :heart: by [Frameright](https://frameright.io). Power
to the pictures!

> **Less than 5kB in your final client-side bundle.**
  :sparkles: [Live demo](https://react.frameright.io)

  💻 [CodeSandbox](https://codesandbox.io/s/image-display-control-react-component-m6qj9r)
Expand Down Expand Up @@ -144,6 +146,8 @@ In your Node.js-based project (e.g. using [Next.js](https://nextjs.org/) or
npm install @frameright/react-image-display-control
```

> **Less than 5kB in your final client-side bundle.**
  :floppy_disk:
[Importing in your project](https://docs.frameright.io/react/importing)

Expand Down
8 changes: 4 additions & 4 deletions example/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ const App = () => {
>
<ImageDisplayControl data-debug>
<img
src="https://react.frameright.io/assets/pics/skater.jpg"
alt="Skater"
src="https://webc.frameright.io/assets/pics/skater1_highres.jpg"
alt="Skater 1"
style={{
width: '50%',
height: '100%',
verticalAlign: 'top', // avoid spurious margin when height becomes smaller than the font size
}}
/>
<img
src="https://webc.frameright.io/assets/pics/birds.jpg"
alt="Birds"
src="https://webc.frameright.io/assets/pics/skater2.jpg"
alt="Skater 2"
style={{
width: '50%',
height: '100%',
Expand Down
6 changes: 2 additions & 4 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@
"devDependencies": {
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"events": "^3.3.0",
"buffer": "^6.0.3",
"parcel": "^2.9.1",
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"typescript": "^5.0.4",
"util": "^0.12.5"
"typescript": "^5.0.4"
}
}
2,808 changes: 1,169 additions & 1,639 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"react-component",
"reactjs"
],
"version": "1.0.5",
"version": "1.0.6",
"license": "MIT",
"repository": "https://github.com/Frameright/react-image-display-control",
"author": {
Expand Down Expand Up @@ -80,7 +80,7 @@
"typescript": "^5.2.0"
},
"dependencies": {
"@frameright/image-display-control-metadata-parser": "^1.1.0",
"@frameright/image-display-control-metadata-parser": "^2.0.0",
"@frameright/image-display-control-web-component": "^1.1.7",
"uuid": "^9.0.0"
}
Expand Down
135 changes: 31 additions & 104 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@ import { v4 as uuidv4 } from 'uuid';

import * as React from 'react';

// Note: this import is successful only if "moduleResolution" is set to "Node"
// in tsconfig.json. However if this is set to "bundler" instead and this can't
// be resolved, the entire code is still type-valid with `Parser` being of type
// `any`.
import type { Parser } from '@frameright/image-display-control-metadata-parser/dist/index.d';
import { Parser } from '@frameright/image-display-control-metadata-parser';

// If true, we are either running on the server at request-time, or at
// build time, building a static (e.g. Next.js) page.
const isServerOrStatic = typeof window === 'undefined';

const isBrowser = !isServerOrStatic;

// See https://stackoverflow.com/questions/50940640/how-to-determine-if-jest-is-running-the-code-or-not
const isRunningTests = process.env.JEST_WORKER_ID !== undefined;
let isRunningTests = false;
try {
// See https://stackoverflow.com/questions/50940640/how-to-determine-if-jest-is-running-the-code-or-not
isRunningTests = process.env.JEST_WORKER_ID !== undefined;
} catch (e) {
// Probably "ReferenceError: process is not defined", ignore.
}

// See https://docs.frameright.io/web-component/importing
if (isBrowser && !isRunningTests) {
Expand All @@ -25,67 +26,24 @@ if (isBrowser && !isRunningTests) {
);
}

type ParserConstructor = {
new (buffer: Buffer): Parser;
};

// In the browser, importing image-display-control-metadata-parser-standalone
// will define window.ImageDisplayControl and window.Buffer.
interface ExtendedWindow extends Window {
Buffer: { Buffer: BufferConstructor } | undefined;
ImageDisplayControl:
| {
Parser: ParserConstructor;
}
| undefined;
}

interface FsModule {
readFileSync: (path: string) => Buffer;
}

let fs: FsModule | null = null;
let parserConstructor: ParserConstructor | null = null;
if (isBrowser) {
// Defines window.ImageDisplayControl and window.Buffer.

// Note: this has no .d.ts file, so we have to use `"noImplicitAny": false`
// in tsconfig.json for this import to work. Ideally we should get rid of
// this special standalone file, as it ships Node polyfills for the browser.
// Instead, the library should not require Node polyfills. See
// https://github.com/Frameright/image-display-control-metadata-parser/issues/3
import(
'@frameright/image-display-control-metadata-parser/dist/image-display-control-metadata-parser-standalone.min.js'
).then(() => {
const extendedWindow = window as unknown as ExtendedWindow;
if (extendedWindow.ImageDisplayControl) {
parserConstructor = extendedWindow.ImageDisplayControl.Parser;
}
});
} else {
// We need to use `require()` here with vite-plugin-ssr in prod as otherwise
if (!isBrowser) {
// We need to use `require()` here with Vite/Vike in prod as otherwise
// `import()` will happen later than the server-side rendering. However
// `require()` isn't defined in dev mode, so then we fall back to `import()`.
if (require) {
// FIXME: for this to work on Next.js, we need to set
// `config.resolve.fallback = { fs: false };` in next.config.js. See
// * https://github.com/Frameright/image-display-control-metadata-parser/issues/3
// * https://stackoverflow.com/questions/64926174/module-not-found-cant-resolve-fs-in-next-js-application
// https://stackoverflow.com/questions/64926174/module-not-found-cant-resolve-fs-in-next-js-application
fs = require('fs');

// eslint-disable-next-line @typescript-eslint/no-var-requires
const parserModule = require('@frameright/image-display-control-metadata-parser');

parserConstructor = parserModule.Parser;
} else {
import('fs').then((fsModule) => {
fs = fsModule;
});
import('@frameright/image-display-control-metadata-parser').then(
(parserModule) => {
parserConstructor = parserModule.Parser;
}
);
}
}

Expand Down Expand Up @@ -145,7 +103,16 @@ export function ImageDisplayControl({

++numImgChildren;

const newAttrs = {
const newAttrs: {
is: string;
class: string;
ref: React.MutableRefObject<null>;
'data-idc-uuid': string;
suppressHydrationWarning: boolean;
'data-src-prop': string;
'data-path-on-server'?: string;
'data-image-regions'?: string;
} = {
// Note: thanks to this, react will recognize this as a custom element.
// https://react.dev/reference/react-dom/components#custom-html-elements
is: 'image-display-control',
Expand Down Expand Up @@ -337,7 +304,7 @@ async function _populateImageRegionsMap(
}

await _waitForImports(debug);
const regions = _readImageRegionsJsonFromArrayBuffer(arrayBuffer);
const regions = _readImageRegionsJsonFromBuffer(arrayBuffer);
if (regions) {
_traceIfDebug(
debug,
Expand Down Expand Up @@ -431,42 +398,8 @@ function _readImageRegionsJsonFromDisk(
return result;
}

// To be used only on client-side.
function _readImageRegionsJsonFromArrayBuffer(
arrayBuffer: ArrayBuffer
): string {
let result = '';
try {
const extendedWindow = window as unknown as ExtendedWindow;
if (!extendedWindow.Buffer) {
// See
// https://github.com/Frameright/image-display-control-metadata-parser/issues/3
_warn(
"Buffer module not available, can't read image regions from",
'ArrayBuffer.'
);
} else {
const buffer = extendedWindow.Buffer.Buffer.from(arrayBuffer);
result = _readImageRegionsJsonFromBuffer(buffer);
}
} catch (error) {
_warn('Error while reading image regions from array buffer:', error);
}
return result;
}

function _readImageRegionsJsonFromBuffer(buffer: Buffer): string {
if (!parserConstructor) {
// See
// https://github.com/Frameright/image-display-control-metadata-parser/issues/3
_warn(
"parserConstructor module not available, can't read image regions from",
'Buffer.'
);
return '';
}

const parser = new parserConstructor(buffer);
function _readImageRegionsJsonFromBuffer(buffer: Buffer | ArrayBuffer): string {
const parser = new Parser(buffer);
const regions = parser.getIdcMetadata('rectangle', 'crop');
return JSON.stringify(regions);
}
Expand Down Expand Up @@ -509,8 +442,8 @@ function _getImageSource(

result = {
src:
element.attributes['data-src-prop']?.value ||
element.attributes['src']?.value ||
element.attributes.getNamedItem('data-src-prop')?.value ||
element.attributes.getNamedItem('src')?.value ||
element.src,
};
}
Expand All @@ -528,21 +461,15 @@ function _isImgElement(
return !!element.props.src;
} else if ('attributes' in element) {
// HTML element
return !!element.attributes['src'].value;
return !!element.attributes.getNamedItem('src')?.value;
}
return false;
}

// Workaround for
// https://github.com/Frameright/image-display-control-metadata-parser/issues/3
// FIXME: workaround for async import() on Vite/Vike in dev mode.
async function _waitForImports(debug: boolean) {
const extendedWindow = window as unknown as ExtendedWindow;
for (const waitMs of [100, 200, 500, 1000, 1000]) {
if (
(fs || isBrowser) &&
(extendedWindow.Buffer || isServerOrStatic) &&
parserConstructor
) {
if (fs || isBrowser) {
_traceIfDebug(debug, 'All imports have resolved.');
return;
}
Expand All @@ -554,11 +481,11 @@ async function _waitForImports(debug: boolean) {

const consolePrefix = '[idc]';

function _warn(...args) {
function _warn(...args: any[]) {
console.warn(consolePrefix, '[warn]', ...args);
}

function _traceIfDebug(debug: boolean, ...args) {
function _traceIfDebug(debug: boolean, ...args: any[]) {
if (debug) {
console.log(consolePrefix, '[debug]', ...args);
}
Expand Down
10 changes: 10 additions & 0 deletions test/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import * as React from 'react';
import { createRoot } from 'react-dom/client';

// Workaround for "ReferenceError: TextDecoder is not defined". See
// * https://stackoverflow.com/questions/68468203/why-am-i-getting-textencoder-is-not-defined-in-jest
// * https://mattbatman.com/textencoder-textdecoder-jest-and-jsdom/
import { TextDecoder } from 'util';
Object.defineProperty(window, 'TextDecoder', {
writable: true,
value: TextDecoder,
});
// eslint-disable-next-line import/first
import { ImageDisplayControl } from '../src';

describe('it', () => {
Expand Down
6 changes: 1 addition & 5 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@
"strict": true,
// linter checks for common issues
"noImplicitReturns": true,

// Needed for importing image-display-control-metadata-parser-standalone in
// src/index.tsx
"noImplicitAny": false,

"noImplicitAny": true,
"noFallthroughCasesInSwitch": true,
// noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative
"noUnusedLocals": true,
Expand Down

0 comments on commit 8131ae8

Please sign in to comment.