Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export placeholders #324

Merged
merged 7 commits into from
Nov 10, 2022
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 29.7.0

- Export jsenvPluginPlaceholders

# 29.6.1

- Fix error introduced in 29.6.0 on dev server
Expand Down
2 changes: 1 addition & 1 deletion packages/jsenv-plugin-react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jsenv/plugin-react",
"version": "1.1.5",
"version": "1.1.6",
"license": "MIT",
"repository": {
"type": "git",
Expand Down
4 changes: 4 additions & 0 deletions packages/jsenv-plugin-react/src/jsenv_plugin_react.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export const jsenvPluginReact = ({
"/**/node_modules/react/jsx-runtime/": { external: ["react"] },
"/**/node_modules/react/jsx-dev-runtime": { external: ["react"] },
"/**/react-refresh/": { external: ["react"] },
// in case redux is used
"/**/node_modules/react-is/": true,
"/**/node_modules/use-sync-external-store/": { external: ["react"] },
"/**/node_modules/hoist-non-react-statics/": { external: ["react-is"] },
},
}),
jsenvPluginReactRefreshPreamble(),
Expand Down
2 changes: 1 addition & 1 deletion packages/urls/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jsenv/urls",
"version": "1.2.7",
"version": "1.2.8",
"license": "MIT",
"repository": {
"type": "git",
Expand Down
9 changes: 6 additions & 3 deletions packages/urls/src/url_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import { urlToExtension } from "./url_to_extension.js"
import { urlToResource } from "./url_to_resource.js"

export const asUrlWithoutSearch = (url) => {
const urlObject = new URL(url)
urlObject.search = ""
return urlObject.href
if (url.includes("?")) {
const urlObject = new URL(url)
urlObject.search = ""
return urlObject.href
}
return url
}

export const isValidUrl = (url) => {
Expand Down
4 changes: 2 additions & 2 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export { startBuildServer } from "./build/start_build_server.js"

// helpers
export { pingServer } from "./ping_server.js"
export { replacePlaceholders } from "./helpers/replace_placeholders.js"

// advanced
export { execute } from "./execute/execute.js"
export { jsenvPluginInjectGlobals } from "./plugins/inject_globals/jsenv_plugin_inject_globals.js"
export { jsenvPluginPlaceholders } from "./plugins/placeholders/jsenv_plugin_placeholders.js"
export { execute } from "./execute/execute.js"
28 changes: 20 additions & 8 deletions src/plugins/inject_globals/jsenv_plugin_inject_globals.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
import { URL_META } from "@jsenv/url-meta"
import { asUrlWithoutSearch } from "@jsenv/urls"

import { injectGlobals } from "./inject_globals.js"

export const jsenvPluginInjectGlobals = (urlAssociations) => {
export const jsenvPluginInjectGlobals = (rawAssociations) => {
let resolvedAssociations

return {
name: "jsenv:inject_globals",
appliesDuring: "*",
init: (context) => {
resolvedAssociations = URL_META.resolveAssociations(
{ injector: rawAssociations },
context.rootDirectoryUrl,
)
},
transformUrlContent: async (urlInfo, context) => {
const url = Object.keys(urlAssociations).find((url) => {
return url === urlInfo.url
const { injector } = URL_META.applyAssociations({
url: asUrlWithoutSearch(urlInfo.url),
associations: resolvedAssociations,
})
if (!url) {
if (!injector) {
return null
}
let globals = urlAssociations[url]
if (typeof globals === "function") {
globals = await globals(urlInfo, context)
if (typeof injector !== "function") {
throw new TypeError("injector must be a function")
}
if (Object.keys(globals).length === 0) {
const globals = await injector(urlInfo, context)
if (!globals || Object.keys(globals).length === 0) {
return null
}
return injectGlobals(urlInfo, globals)
Expand Down
51 changes: 27 additions & 24 deletions src/plugins/minification/jsenv_plugin_minification.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,37 +37,40 @@ export const jsenvPluginMinification = (minification) => {
options: minification.json,
})
: null
const cssOptimizer = minification.css
? (urlInfo, context) =>
minifyCss({
cssUrlInfo: urlInfo,
context,
options: minification.css,
})
: null
const jsClassicOptimizer = minification.js_classic
? (urlInfo, context) =>
minifyJs({
jsUrlInfo: urlInfo,
context,
options: minification.js_classic,
})
: null
const jsModuleOptimizer = minification.js_module
? (urlInfo, context) =>
minifyJs({
jsUrlInfo: urlInfo,
context,
options: minification.js_module,
})
: null

return {
name: "jsenv:minification",
appliesDuring: "build",
optimizeUrlContent: {
html: htmlOptimizer,
svg: htmlOptimizer,
css: minification.css
? (urlInfo, context) =>
minifyCss({
cssUrlInfo: urlInfo,
context,
options: minification.css,
})
: null,
js_classic: minification.js_classic
? (urlInfo, context) =>
minifyJs({
jsUrlInfo: urlInfo,
context,
options: minification.js_classic,
})
: null,
js_module: minification.js_module
? (urlInfo, context) =>
minifyJs({
jsUrlInfo: urlInfo,
context,
options: minification.js_module,
})
: null,
css: cssOptimizer,
js_classic: jsClassicOptimizer,
js_module: jsModuleOptimizer,
json: jsonOptimizer,
importmap: jsonOptimizer,
webmanifest: jsonOptimizer,
Expand Down
36 changes: 36 additions & 0 deletions src/plugins/placeholders/jsenv_plugin_placeholders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { URL_META } from "@jsenv/url-meta"
import { asUrlWithoutSearch } from "@jsenv/urls"

import { replacePlaceholders } from "./replace_placeholders.js"

export const jsenvPluginPlaceholders = (rawAssociations) => {
let resolvedAssociations

return {
name: "jsenv:placeholders",
appliesDuring: "*",
init: (context) => {
resolvedAssociations = URL_META.resolveAssociations(
{ replacer: rawAssociations },
context.rootDirectoryUrl,
)
},
transformUrlContent: async (urlInfo, context) => {
const { replacer } = URL_META.applyAssociations({
url: asUrlWithoutSearch(urlInfo.url),
associations: resolvedAssociations,
})
if (!replacer) {
return null
}
if (typeof replacer !== "function") {
throw new TypeError("replacer must be a function")
}
const replacements = await replacer(urlInfo, context)
if (!replacements || Object.keys(replacements).length === 0) {
return null
}
return replacePlaceholders(urlInfo, replacements)
},
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { createMagicSource } from "@jsenv/sourcemap"

export const replacePlaceholders = (content, replacements) => {
export const replacePlaceholders = (urlInfo, replacements) => {
const content = urlInfo.content
const magicSource = createMagicSource(content)
Object.keys(replacements).forEach((key) => {
let index = content.indexOf(key)
while (index !== -1) {
const start = index
const end = index + key.length
magicSource.replace({ start, end, replacement: replacements[key] })
magicSource.replace({
start,
end,
replacement:
urlInfo.type === "js_classic" || urlInfo.type === "js_module"
? JSON.stringify(replacements[key], null, " ")
: replacements[key],
})
index = content.indexOf(key, end)
}
})
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/url_version/jsenv_plugin_url_version.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export const jsenvPluginUrlVersion = () => {
return {
name: "jsenv:url_version",
appliesDuring: "*",
appliesDuring: "dev",
redirectUrl: (reference) => {
// "v" search param goal is to enable long-term cache
// for server response headers
Expand Down
11 changes: 7 additions & 4 deletions tests/__internal__/replace_placerholders.test.mjs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { assert } from "@jsenv/assert"

import { replacePlaceholders } from "@jsenv/core"
import { replacePlaceholders } from "@jsenv/core/src/plugins/placeholders/replace_placeholders.js"

const result = replacePlaceholders(
`const foo = __FOO__
{
type: "js_module",
content: `const foo = __FOO__
const t = __FOO__
const bar = __BAR__`,
},
{
__FOO__: JSON.stringify("hello"),
__BAR__: JSON.stringify("world"),
__FOO__: "hello",
__BAR__: "world",
},
)
const actual = result.content
Expand Down
16 changes: 16 additions & 0 deletions tests/dev_and_build/inject_globals/client/main.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<meta charset="utf8" />
<link rel="icon" href="data:," />
</head>
<body>
<script>
window.resultPromise = new Promise((resolve) => {
window.resolveResultPromise = resolve
})
</script>
<script src="./main.js?v=10"></script>
</body>
</html>
1 change: 1 addition & 0 deletions tests/dev_and_build/inject_globals/client/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
window.resolveResultPromise(window.__DEMO__)
37 changes: 37 additions & 0 deletions tests/dev_and_build/inject_globals/inject_globals_build.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { assert } from "@jsenv/assert"

import { build } from "@jsenv/core"
import { startFileServer } from "@jsenv/core/tests/start_file_server.js"
import { executeInChromium } from "@jsenv/core/tests/execute_in_chromium.js"
import { plugins } from "./jsenv_config.mjs"

const test = async (params) => {
await build({
logLevel: "warn",
rootDirectoryUrl: new URL("./client/", import.meta.url),
buildDirectoryUrl: new URL("./dist/", import.meta.url),
entryPoints: {
"./main.html": "main.html",
},
plugins,
minification: false,
...params,
})
const server = await startFileServer({
rootDirectoryUrl: new URL("./dist/", import.meta.url),
})
const { returnValue } = await executeInChromium({
url: `${server.origin}/main.html`,
/* eslint-disable no-undef */
pageFunction: async () => {
return window.resultPromise
},
/* eslint-enable no-undef */
})

const actual = returnValue
const expected = "build"
assert({ actual, expected })
}

await test()
29 changes: 29 additions & 0 deletions tests/dev_and_build/inject_globals/inject_globals_dev.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { assert } from "@jsenv/assert"

import { startDevServer } from "@jsenv/core"
import { executeInChromium } from "@jsenv/core/tests/execute_in_chromium.js"
import { plugins } from "./jsenv_config.mjs"

const test = async (params) => {
const devServer = await startDevServer({
logLevel: "warn",
rootDirectoryUrl: new URL("./client/", import.meta.url),
keepProcessAlive: false,
plugins,
...params,
})
const { returnValue } = await executeInChromium({
url: `${devServer.origin}/main.html`,
/* eslint-disable no-undef */
pageFunction: async () => {
return window.resultPromise
},
/* eslint-enable no-undef */
})

const actual = returnValue
const expected = "dev"
assert({ actual, expected })
}

await test()
9 changes: 9 additions & 0 deletions tests/dev_and_build/inject_globals/jsenv_config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { jsenvPluginInjectGlobals } from "@jsenv/core"

export const plugins = [
jsenvPluginInjectGlobals({
"./main.html": (urlInfo, context) => {
return { __DEMO__: context.scenarios.dev ? "dev" : "build" }
},
}),
]
16 changes: 16 additions & 0 deletions tests/dev_and_build/placeholders/client/main.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<meta charset="utf8" />
<link rel="icon" href="data:," />
</head>
<body>
<script>
window.resultPromise = new Promise((resolve) => {
window.resolveResultPromise = resolve
})
</script>
<script src="./main.js?foo=bar"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions tests/dev_and_build/placeholders/client/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// eslint-disable-next-line no-undef
window.resolveResultPromise(__DEMO__)
11 changes: 11 additions & 0 deletions tests/dev_and_build/placeholders/jsenv_config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { jsenvPluginPlaceholders } from "@jsenv/core"

export const plugins = [
jsenvPluginPlaceholders({
"./main.js": (urlInfo, context) => {
return {
__DEMO__: context.scenarios.dev ? "dev" : "build",
}
},
}),
]
Loading