Skip to content

Commit

Permalink
feat: utilize timestamps in build and renderDocument for AUS (#6964)
Browse files Browse the repository at this point in the history
* feat: utilize timestamps in build and renderDocument for AUS

* refactor: Apply suggestions from code review

Co-authored-by: Espen Hovlandsdal <[email protected]>

* fix: set to no-cache for help with consistency

---------

Co-authored-by: Espen Hovlandsdal <[email protected]>
  • Loading branch information
ricokahler and rexxars authored Jun 25, 2024
1 parent f2ddfa7 commit a1da8cc
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 24 deletions.
1 change: 1 addition & 0 deletions packages/sanity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
"@types/configstore": "^5.0.1",
"@types/connect-history-api-fallback": "^1.5.2",
"@types/debug": "^4.1.12",
"@types/jsdom": "^20.0.0",
"@types/lodash": "^4.14.149",
"@types/log-symbols": "^2.0.0",
"@types/node": "^18.19.8",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {describe, expect, it} from '@jest/globals'
import {JSDOM} from 'jsdom'
import {renderToStaticMarkup} from 'react-dom/server'

import {_prefixUrlWithBasePath, addImportMapToHtml} from '../renderDocument'
import {TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT} from '../constants'
import {_prefixUrlWithBasePath, addTimestampedImportMapScriptToHtml} from '../renderDocument'

describe('_prefixUrlWithBasePath', () => {
describe('when basePath is default value of "/"', () => {
Expand Down Expand Up @@ -69,13 +71,69 @@ describe('_prefixUrlWithBasePath', () => {
})
})

describe('addImportMapToHtml', () => {
describe('addTimestampedImportMapScriptToHtml', () => {
const importMap = {
imports: {
react: 'https://example.com/react',
},
}

it('takes the import map from the `#__imports` script tag synchronously creates an importmap', () => {
const importMapWithSanityTimestamps = {
...importMap,
imports: {
...importMap.imports,
'sanity': 'https://sanity-cdn.work/v1/modules/sanity/default/%5E3.40.0/t12345',
'sanity/': 'https://sanity-cdn.work/v1/modules/sanity/default/%5E3.40.0/t12345/',
'@sanity/vision':
'https://sanity-cdn.work/v1/modules/@sanity__vision/default/%5E3.40.0/t12345',
'@sanity/vision/':
'https://sanity-cdn.work/v1/modules/@sanity__vision/default/%5E3.40.0/t12345/',
},
}

const input = `<html lang="en">
<head><meta charSet="utf-8" /><title>Sanity Studio</title></head>
<body><div id="sanity"/></body>
</html>`

const output = `<html lang="en">
<head><meta charSet="utf-8" ><title>Sanity Studio</title><script type="application/json" id="__imports">{"imports":{"react":"https://example.com/react","sanity":"https://sanity-cdn.work/v1/modules/sanity/default/%5E3.40.0/t12345","sanity/":"https://sanity-cdn.work/v1/modules/sanity/default/%5E3.40.0/t12345/","@sanity/vision":"https://sanity-cdn.work/v1/modules/@sanity__vision/default/%5E3.40.0/t12345","@sanity/vision/":"https://sanity-cdn.work/v1/modules/@sanity__vision/default/%5E3.40.0/t12345/"}}</script>${TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT}</head>
<body><div id="sanity"></div></body>
</html>`

expect(addTimestampedImportMapScriptToHtml(input, importMapWithSanityTimestamps)).toBe(output)

const {document} = new JSDOM(output, {runScripts: 'dangerously'}).window
const staticImportMap = JSON.parse(document.querySelector('#__imports')?.textContent as string)
const runtimeImportMap = JSON.parse(
document.querySelector('script[type="importmap"]')?.textContent as string,
)

expect(runtimeImportMap).toMatchObject({
imports: {
'react': 'https://example.com/react',
'sanity': expect.stringMatching(
/^https:\/\/sanity-cdn\.work\/v1\/modules\/sanity\/default\/%5E3\.40\.0\/t\d+$/,
),
'sanity/': expect.stringMatching(
// notice the trailing slash here
/^https:\/\/sanity-cdn\.work\/v1\/modules\/sanity\/default\/%5E3\.40\.0\/t\d+\/$/,
),
'@sanity/vision': expect.stringMatching(
/^https:\/\/sanity-cdn\.work\/v1\/modules\/@sanity__vision\/default\/%5E3\.40\.0\/t\d+$/,
),
'@sanity/vision/': expect.stringMatching(
// notice the trailing slash here
/^https:\/\/sanity-cdn\.work\/v1\/modules\/@sanity__vision\/default\/%5E3\.40\.0\/t\d+\/$/,
),
},
})

// ensures that the timestamps have actually been replaced
expect(staticImportMap).not.toEqual(runtimeImportMap)
})

it('takes in an existing HTML document and adds the given import map to the end of the head of the document', () => {
const input = renderToStaticMarkup(
<html lang="en">
Expand All @@ -88,26 +146,22 @@ describe('addImportMapToHtml', () => {
</body>
</html>,
)
const output = addImportMapToHtml(input, importMap)
const output = `<html lang="en"><head><meta charSet="utf-8"><title>Sanity Studio</title><script type="application/json" id="__imports">{"imports":{"react":"https://example.com/react"}}</script>${TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT}</head><body><div id="sanity"></div></body></html>`

expect(output).toBe(
'<html lang="en"><head><meta charSet="utf-8"><title>Sanity Studio</title><script type="importmap">{"imports":{"react":"https://example.com/react"}}</script></head><body><div id="sanity"></div></body></html>',
)
expect(addTimestampedImportMapScriptToHtml(input, importMap)).toBe(output)
})

it('creates an <html> element if none exist', () => {
const input = 'foo<div>bar</div>baz'
const output =
'<html><head><script type="importmap">{"imports":{"react":"https://example.com/react"}}</script></head>foo<div>bar</div>baz</html>'
const output = `<html><head><script type="application/json" id="__imports">{"imports":{"react":"https://example.com/react"}}</script>${TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT}</head>foo<div>bar</div>baz</html>`

expect(addImportMapToHtml(input, importMap)).toBe(output)
expect(addTimestampedImportMapScriptToHtml(input, importMap)).toBe(output)
})

it('creates a <head> to the document if one does not exist', () => {
const input = '<html><body><script src="index.js"></script></body></html>'
const output =
'<html><head><script type="importmap">{"imports":{"react":"https://example.com/react"}}</script></head><body><script src="index.js"></script></body></html>'
const output = `<html><head><script type="application/json" id="__imports">{"imports":{"react":"https://example.com/react"}}</script>${TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT}</head><body><script src="index.js"></script></body></html>`

expect(addImportMapToHtml(input, importMap)).toBe(output)
expect(addTimestampedImportMapScriptToHtml(input, importMap)).toBe(output)
})
})
36 changes: 36 additions & 0 deletions packages/sanity/src/_internal/cli/server/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* This script takes the import map from the `#__imports` script tag,
* modifies relevant URLs that match the sanity-cdn hostname by replacing
* the existing timestamp in the sanity-cdn URLs with a new runtime timestamp,
* and injects the modified import map back into the HTML.
*
* This will be injected into the HTML of the user's bundle.
*
* Note that this is in a separate constants file to prevent "Cannot access
* before initialization" errors.
*/
export const TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT = `<script>
// auto-generated script to add import map with timestamp
const importsJson = document.getElementById('__imports')?.textContent;
const { imports = {}, ...rest } = importsJson ? JSON.parse(importsJson) : {};
const importMapEl = document.createElement('script');
importMapEl.type = 'importmap';
const newTimestamp = \`/t\${Math.floor(Date.now() / 1000)}\`;
importMapEl.textContent = JSON.stringify({
imports: Object.fromEntries(
Object.entries(imports).map(([specifier, path]) => {
try {
const url = new URL(path);
if (/^sanity-cdn\\.[a-zA-Z]+$/.test(url.hostname)) {
url.pathname = url.pathname.replace(/\\/t\\d+/, newTimestamp);
}
return [specifier, url.toString()];
} catch {
return [specifier, path];
}
})
),
...rest,
});
document.head.appendChild(importMapEl);
</script>`
8 changes: 5 additions & 3 deletions packages/sanity/src/_internal/cli/server/renderDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {createElement} from 'react'
import {renderToStaticMarkup} from 'react-dom/server'

import {getAliases} from './aliases'
import {TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT} from './constants'
import {debug as serverDebug} from './debug'
import {type SanityMonorepo} from './sanityMonorepo'

Expand Down Expand Up @@ -226,7 +227,7 @@ function getDocumentHtml(
})

debug('Rendering document component using React')
const result = addImportMapToHtml(
const result = addTimestampedImportMapScriptToHtml(
renderToStaticMarkup(createElement(Document, {...defaultProps, ...props, css})),
importMap,
)
Expand All @@ -237,7 +238,7 @@ function getDocumentHtml(
/**
* @internal
*/
export function addImportMapToHtml(
export function addTimestampedImportMapScriptToHtml(
html: string,
importMap?: {imports?: Record<string, string>},
): string {
Expand All @@ -261,8 +262,9 @@ export function addImportMapToHtml(

headEl.insertAdjacentHTML(
'beforeend',
`<script type="importmap">${JSON.stringify(importMap)}</script>`,
`<script type="application/json" id="__imports">${JSON.stringify(importMap)}</script>`,
)
headEl.insertAdjacentHTML('beforeend', TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT)
return root.outerHTML
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ const MODULES_HOST =
* @internal
*/
export function getAutoUpdateImportMap(version: string): AutoUpdatesImportMap {
const timestamp = `t${Math.floor(Date.now() / 1000)}`

const autoUpdatesImports = {
'sanity': `${MODULES_HOST}/v1/modules/sanity/default/${version}`,
'sanity/': `${MODULES_HOST}/v1/modules/sanity/default/${version}/`,
'@sanity/vision': `${MODULES_HOST}/v1/modules/@sanity__vision/default/${version}`,
'@sanity/vision/': `${MODULES_HOST}/v1/modules/@sanity__vision/default/${version}/`,
'sanity': `${MODULES_HOST}/v1/modules/sanity/default/${version}/${timestamp}`,
'sanity/': `${MODULES_HOST}/v1/modules/sanity/default/${version}/${timestamp}/`,
'@sanity/vision': `${MODULES_HOST}/v1/modules/@sanity__vision/default/${version}/${timestamp}`,
'@sanity/vision/': `${MODULES_HOST}/v1/modules/@sanity__vision/default/${version}/${timestamp}/`,
}

return autoUpdatesImports
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

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

13 changes: 8 additions & 5 deletions scripts/uploadBundles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ async function copyPackages() {

interface ManifestPackage {
default: string
versions: string[]
versions: {version: string; timestamp: number}[]
}

interface Manifest {
Expand All @@ -111,7 +111,9 @@ async function updateManifest(newVersions: Map<string, string>) {
console.log('Existing manifest not found', error)
}

// Add the new version to the manifest
const timestamp = Math.floor(Date.now() / 1000)

// Add the new version to the manifest with timestamp
const newManifest = Array.from(newVersions).reduce((initial, [key, value]) => {
const dirName = cleanDirName(key)

Expand All @@ -122,7 +124,7 @@ async function updateManifest(newVersions: Map<string, string>) {
[dirName]: {
...initial.packages[dirName],
default: value,
versions: [...(initial.packages[dirName]?.versions || []), value],
versions: [...(initial.packages[dirName]?.versions || []), {version: value, timestamp}],
},
},
}
Expand All @@ -135,8 +137,9 @@ async function updateManifest(newVersions: Map<string, string>) {
destination: 'modules/v1/manifest-v1.json',
contentType: 'application/json',
metadata: {
// 10 seconds
cacheControl: 'public, max-age=10',
// no-cache to help with consistency across pods when this manifest
// is downloaded in the module-server
cacheControl: 'no-cache, max-age=0',
},
}

Expand Down

0 comments on commit a1da8cc

Please sign in to comment.