-
Notifications
You must be signed in to change notification settings - Fork 0
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
Feature/vite #2
base: main
Are you sure you want to change the base?
Feature/vite #2
Changes from 4 commits
dee986e
a694c90
c3f65df
b8e9368
2ef530a
a31fe48
b80bc3b
9d39048
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
# base-href-runtime-webpack-plugin | ||
# base-href-runtime-vite-plugin | ||
|
||
[![npm version](https://badge.fury.io/js/base-href-runtime-webpack-plugin.svg)](https://badge.fury.io/js/base-href-runtime-webpack-plugin) | ||
[![npm version](https://badge.fury.io/js/base-href-runtime-vite-plugin.svg)](https://badge.fury.io/js/base-href-runtime-vite-plugin) | ||
|
||
Extension for [html-webpack-plugin](https://github.com/ampedandwired/html-webpack-plugin) to programmatically insert or update [`<base href="...">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag **in runtime** depending on *window.location.pathname*. | ||
Extension for [html-vite-plugin](https://github.com/ampedandwired/html-vite-plugin) to programmatically insert or update [`<base href="...">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag **in runtime** depending on _window.location.pathname_. | ||
|
||
It inserts inline `<script>` in your `index.html` output which generates (rewrites) `<base href="...">` | ||
|
||
|
@@ -13,6 +13,7 @@ When your application is proxied with many different domains and prefixes you mi | |
### Example | ||
|
||
Your application is hosted on `example.com` and you have 2 known entrypoints (proxies) for this application: | ||
|
||
``` | ||
app.test.com/ui/app -> example.com | ||
app2.io/ui/test/entrypoint -> example.com | ||
|
@@ -21,113 +22,75 @@ app2.io/ui/test/entrypoint -> example.com | |
So you want to open `app.test.com/ui/app` and resolve `index.js` request to `app.test.com/ui/app/index.js` (`-> example.com/index.js`) | ||
|
||
For this purpose you want to generate different `<base>` tag: | ||
|
||
```html | ||
<!-- for app.test.com/ui/app/ (/ui/app/) --> | ||
<base href="/ui/app/"> | ||
<script src="index.js" /> <!-- /ui/app/index.js --> | ||
<base href="/ui/app/" /> | ||
<script src="index.js" /> | ||
<!-- /ui/app/index.js --> | ||
|
||
<!-- for app2.io/ui/test/entrypoint (/ui/test/entrypoint/) --> | ||
<base href="/ui/test/entrypoint/"> | ||
<script src="index.js" /> <!-- /ui/test/entrypoint/index.js --> | ||
<base href="/ui/test/entrypoint/" /> | ||
<script src="index.js" /> | ||
<!-- /ui/test/entrypoint/index.js --> | ||
``` | ||
|
||
Thus `<base>` tag for the same `index.html` has to be generated **in runtime**. | ||
|
||
# Installation | ||
|
||
For webpack v5 use latest (^1.1.0): | ||
`npm i --save-dev base-href-runtime-webpack-plugin` | ||
`npm i --save-dev base-href-runtime-vite-plugin` | ||
|
||
# Usage | ||
|
||
Prepare `HtmlWebpackPlugin`, it should generate *relative* paths for assets. | ||
|
||
```diff | ||
new HtmlWebpackPlugin({ | ||
template: 'public/index.html', | ||
filename: 'index.html', | ||
// ..., | ||
- publicPath: '/', | ||
+ publicPath: 'auto', // assets paths must be relative | ||
+ base: '/' // same as fallbackBaseHref, see https://github.com/jantimon/html-webpack-plugin#base-tag | ||
}), | ||
``` | ||
|
||
Init `base-href-runtime-webpack-plugin`: | ||
Init `base-href-runtime-vite-plugin`: | ||
|
||
```js | ||
const BaseHrefRuntimeWebpackPlugin = require('base-href-runtime-webpack-plugin'); | ||
|
||
plugins: [ | ||
// ..., | ||
new BaseHrefRuntimeWebpackPlugin({ | ||
fallbackBaseHref: '/', // in case when we didn't match location.pathname | ||
publicPaths: [ // availabled prefixes. Order is important! | ||
'/ui/app/', // <base href="/ui/app/"> | ||
'/ui/test/entrypoint/', // <base href="/ui/test/entrypoint/"> | ||
'/a/b/c/d/e/', // <base href="/a/b/c/d/e/"> | ||
], | ||
}), | ||
] | ||
// vite.config.js | ||
import { defineConfig } from "vite"; | ||
import baseHrefRuntimeVitePlugin from "base-href-runtime-vite-plugin"; | ||
|
||
export default defineConfig({ | ||
plugins: [ | ||
baseHrefRuntimeVitePlugin({ | ||
fallbackBaseHref: "/", // in case when we didn't match location.pathname | ||
publicPaths: [ | ||
// availabled prefixes. Order is important! | ||
"/ui/app/log/", // <base href="/ui/app/log"> | ||
"/ui/app/", // <base href="/ui/app/"> | ||
"/ui/test/entrypoint/", // <base href="/ui/test/entrypoint/"> | ||
"/a/b/c/d/e/", // <base href="/a/b/c/d/e/"> | ||
], | ||
}), | ||
], | ||
}); | ||
``` | ||
|
||
It will inject `<script></script>` in your `index.html`. This script compares current `window.location.pathname` and provided `publicPaths`. Then it updates `<base href="...">` if we have a match. Otherwise it sets *fallbackBaseHref* value in your `<base href="...">` | ||
It will inject `<script></script>` in your `index.html`. This script compares current `window.location.pathname` and provided `publicPaths`. Then it updates `<base href="...">` if we have a match. Otherwise it sets _fallbackBaseHref_ value in your `<base href="...">` | ||
|
||
Plugin **leaves your template untouched** if `fallbackBaseHref` and `publicPaths` options are not provided. | ||
|
||
### Setup application router (optional) | ||
crutch12 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
You might want to use `publicPaths` to prepare your application router (`react-router`, `vue-router`, etc.) | ||
|
||
<details> | ||
<summary>Example with react-router</summary> | ||
|
||
```jsx | ||
import * as React from 'react'; | ||
import * as ReactDOM from 'react-dom'; | ||
import { BrowserRouter as Router } from 'react-router-dom'; | ||
import { publicPaths, fallbackBaseHref } from './lib/constants/config'; // use same variable as publicPaths in you webpack.config.js | ||
|
||
const App = ({ basename }) => { | ||
<Router basename={basename}> | ||
{/* ... your app content ... */} | ||
</Router> | ||
} | ||
|
||
const getBasename = (pathname) => { | ||
// @NOTE: You may straightaway return baseURI | ||
// return document.baseURI; | ||
|
||
const publicPath = publicPaths.find(publicPath => pathname.includes(publicPath.replace(/\/$/, ''))); | ||
return publicPath || fallbackBaseHref; | ||
} | ||
|
||
ReactDOM.render(<App basename={getBasename(window.location.pathname)} />, document.getElementById('#app')); | ||
``` | ||
</details> | ||
|
||
# Caveats | ||
|
||
<details> | ||
<summary>Excessive requests (duplicated requests)</summary> | ||
|
||
https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work#preload_scanner | ||
|
||
https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work#preload_scanner | ||
|
||
> The preload scanner will parse through the content available and request high priority resources like CSS, JavaScript, and web fonts. <...> It will retrieve resources in the background so that by the time the main HTML parser reaches requested assets, they may possibly already be in flight, or have been downloaded. | ||
> The preload scanner will parse through the content available and request high priority resources like CSS, JavaScript, and web fonts. <...> It will retrieve resources in the background so that by the time the main HTML parser reaches requested assets, they may possibly already be in flight, or have been downloaded. | ||
|
||
It means that a browser requests all page's resources before you execute any `<script>`. So if your `<base href="...">` tag is being changed by the `<script>` then your browser will **repeat these requests again**. | ||
|
||
It means that a browser requests all page's resources before you execute any `<script>`. So if your `<base href="...">` tag is being changed by the `<script>` then your browser will **repeat these requests again**. | ||
Example: | ||
|
||
Example: | ||
```html | ||
```html | ||
<html> | ||
<head> | ||
<base href="/unknown/"> | ||
<base href="/unknown/" /> | ||
<script type="text/javascript"> | ||
console.log('Initial document.baseURI:', document.baseURI); | ||
document.querySelector('base').href = '/' | ||
console.log('New document.baseURI:', document.baseURI); | ||
console.log("Initial document.baseURI:", document.baseURI); | ||
document.querySelector("base").href = "/"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. почему стиль у всех ковычек всех поменялся? |
||
console.log("New document.baseURI:", document.baseURI); | ||
</script> | ||
<script src="js/index.js"></script> | ||
|
||
|
@@ -136,9 +99,9 @@ You might want to use `publicPaths` to prepare your application router (`react-r | |
<!-- So <script src="..." /> and <script src="..."></script> have different behaviour (WTF?!) --> | ||
</head> | ||
</html> | ||
``` | ||
Chrome's Network tab result (`js/index.js` request duplicated): | ||
``` | ||
|
||
Chrome's Network tab result (`js/index.js` request duplicated): | ||
|
||
<img width="676" alt="chrome_LmDvH3YJzy" src="https://user-images.githubusercontent.com/19373212/152134966-5cd1699b-4951-4a41-bb3a-2a733b1ac754.png"> | ||
</details> | ||
|
@@ -151,9 +114,9 @@ https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base#browser_compatibi | |
|
||
# References | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Зачем убил рефы... |
||
|
||
https://www.npmjs.com/package/base-href-webpack-plugin | ||
https://www.npmjs.com/package/base-href-vite-plugin | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ну это совсем ложь) И ниже тоже There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @lamtev178 не поправил |
||
|
||
https://github.com/jantimon/html-webpack-plugin#base-tag | ||
https://github.com/jantimon/html-vite-plugin#base-tag | ||
|
||
# Contribution | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,4 @@ | ||
import { Compiler, WebpackPluginInstance } from 'webpack'; | ||
|
||
declare interface BaseHrefRuntimeWebpackPluginOptions { | ||
fallbackBaseHref?: string; | ||
publicPaths?: string[]; | ||
} | ||
|
||
declare class BaseHrefRuntimeWebpackPlugin implements WebpackPluginInstance { | ||
constructor(options: BaseHrefRuntimeWebpackPluginOptions); | ||
|
||
apply: (compiler: Compiler) => void; | ||
} | ||
declare interface baseHrefRuntimeVitePlugin { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Типы же с большой буквы всегда пишем There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Чот я туплю видимо |
||
fallbackBaseHref?: string; | ||
publicPaths?: string[]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,63 +1,47 @@ | ||
const HtmlWebpackPlugin = require('html-webpack-plugin'); | ||
const template = require('lodash.template'); | ||
const fs = require('fs'); | ||
const { validate } = require('schema-utils'); | ||
const schema = require('./schema.json'); | ||
|
||
const scriptTemplate = fs.readFileSync(__dirname + '/script.ejs', 'utf8'); | ||
|
||
module.exports = class BaseHrefRuntimeWebpackPlugin { | ||
constructor(options) { | ||
validate(schema, options, { | ||
name: 'BaseHrefRuntimeWebpackPlugin', | ||
baseDataPath: 'options', | ||
}); | ||
this.options = options; | ||
} | ||
function baseHrefRuntimeVitePlugin(options) { | ||
const fallbackBaseHref = options?.fallbackBaseHref || ""; | ||
const publicPaths = options?.publicPaths || []; | ||
|
||
apply(compiler) { | ||
const publicPaths = (this.options.publicPaths || []).filter(Boolean); | ||
const fallbackBaseHref = this.options.fallbackBaseHref; | ||
if (publicPaths.length === 0 && !fallbackBaseHref) { | ||
return; | ||
} | ||
|
||
if (publicPaths.length === 0 && !fallbackBaseHref) { | ||
return; | ||
} | ||
const scriptTemplateFunction = `(function () { | ||
var publicPaths = [${publicPaths.map( | ||
(path) => "'" + path + "'" | ||
)}] || []; | ||
var fallbackBaseHref = '${fallbackBaseHref}' ? '${fallbackBaseHref}' : 'undefined'; | ||
|
||
const logger = compiler.getInfrastructureLogger('BaseHrefRuntimeWebpackPlugin'); | ||
const scriptTemplateFunction = template(scriptTemplate); | ||
const base = document.createElement("base") | ||
|
||
compiler.hooks.compilation.tap('BaseHrefRuntimeWebpackPlugin', (compilation) => { | ||
HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync('BaseHrefRuntimeWebpackPlugin', (data, callback) => { | ||
if (!data.plugin.options.base) { | ||
logger.warn('You didn\'t specify "base" field in html-webpack-plugin'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning зачем потерял? |
||
} | ||
document.head.append(base) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ты проверял, запросы точно после этого идут верно? Видимо поэтому мы скрипт прямо перед |
||
|
||
const baseTagIndex = data.headTags.findIndex(tag => tag.tagName === 'base'); | ||
const targetIndex = baseTagIndex === -1 ? 0 : (baseTagIndex + 1); | ||
document.querySelector('base').href = publicPaths.find( | ||
(path) => window.location.pathname.includes(path) | ||
) || fallbackBaseHref || document.baseURI})();`; | ||
|
||
const scriptInnerHTML = scriptTemplateFunction({ | ||
publicPaths: publicPaths, | ||
fallbackBaseHref: fallbackBaseHref, | ||
}); | ||
return { | ||
name: "base-href-runtime-vite-plugin", | ||
|
||
data.headTags = [ | ||
...data.headTags.slice(0, targetIndex), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Видно, что мы вставляли скрипт перед тегом @santoslos не помнишь случайно?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ой, сразу после , перепутал работу slice |
||
// @NOTE: Insert our script | ||
transformIndexHtml(html) { | ||
return { | ||
html: html, | ||
tags: [ | ||
{ | ||
tagName: 'script', | ||
tag: "script", | ||
voidTag: false, | ||
meta: { plugin: 'base-href-runtime-webpack-plugin' }, | ||
attributes: { | ||
type: 'text/javascript', | ||
'data-name': 'base-href-runtime-webpack-plugin', | ||
injectTo: "head", | ||
meta: { plugin: "base-href-runtime-webpack-plugin" }, | ||
attrs: { | ||
type: "text/javascript", | ||
"data-name": "base-href-runtime-webpack-plugin", | ||
}, | ||
innerHTML: scriptInnerHTML, | ||
children: scriptTemplateFunction, | ||
}, | ||
...data.headTags.slice(targetIndex) | ||
]; | ||
|
||
callback(null, data); | ||
}); | ||
}); | ||
} | ||
], | ||
}; | ||
}, | ||
}; | ||
} | ||
|
||
module.exports = baseHrefRuntimeVitePlugin; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,23 @@ | ||
{ | ||
"name": "base-href-runtime-webpack-plugin", | ||
"name": "base-href-runtime-vite-plugin", | ||
"version": "1.1.0", | ||
"main": "index.js", | ||
"types": "index.d.ts", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/crutch12/base-href-runtime-webpack-plugin.git" | ||
"url": "https://github.com/web-bee-ru/base-href-runtime-vite-plugin" | ||
}, | ||
"keywords": [ | ||
"base", | ||
"href", | ||
"webpack", | ||
"vite", | ||
"plugin" | ||
], | ||
"scripts": { | ||
"test": "cd ./test && webpack" | ||
"test": "cd ./test && npm install && npm run dev" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. А, ну не лишний в текущей реализации) |
||
}, | ||
"license": "MIT", | ||
"peerDependencies": { | ||
"html-webpack-plugin": ">=4.0.0", | ||
"webpack": ">=4.0.0" | ||
}, | ||
"devDependencies": { | ||
"html-webpack-plugin": "5.5", | ||
"webpack": "^5.23.0", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. А зачем всё перенёс в |
||
"webpack-cli": "^4.9.2" | ||
}, | ||
"dependencies": { | ||
"lodash.template": "^4.5.0", | ||
"schema-utils": "^4.0.0" | ||
"vite": "^4.3.2" | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<!DOCTYPE html> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Пример то репрезентативен? Надо такой пример, чтобы я в network увидел, что у меня запрос идёт в соответствии с base |
||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Vite App</title> | ||
</head> | ||
<body> | ||
<div id="app"></div> | ||
</body> | ||
</html> |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тут обман