зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1810890 - Part 2: update chrome-uri-loader to handle CSS rewriting only r=mstriemer
Depends on D185090 Differential Revision: https://phabricator.services.mozilla.com/D185091
This commit is contained in:
Родитель
df54b95f5b
Коммит
825c3a61d7
|
@ -0,0 +1,145 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* This file contains a webpack loader which rewrites JS source files to use CSS
|
||||
* imports when running in Storybook. This allows JS files loaded in Storybook to use
|
||||
* chrome:// URIs when loading external stylesheets without having to worry
|
||||
* about Storybook being able to find and detect changes to the files.
|
||||
*
|
||||
* This loader allows Lit-based custom element code like this to work with
|
||||
* Storybook:
|
||||
*
|
||||
* render() {
|
||||
* return html`
|
||||
* <link rel="stylesheet" href="chrome://global/content/elements/moz-toggle.css" />
|
||||
* ...
|
||||
* `;
|
||||
* }
|
||||
*
|
||||
* By rewriting the source to this:
|
||||
*
|
||||
* import moztoggleStyles from "toolkit/content/widgets/moz-toggle/moz-toggle.css";
|
||||
* ...
|
||||
* render() {
|
||||
* return html`
|
||||
* <link rel="stylesheet" href=${moztoggleStyles} />
|
||||
* ...
|
||||
* `;
|
||||
* }
|
||||
*
|
||||
* It works similarly for vanilla JS custom elements that utilize template
|
||||
* strings. The following code:
|
||||
*
|
||||
* static get markup() {
|
||||
* return`
|
||||
* <template>
|
||||
* <link rel="stylesheet" href="chrome://browser/skin/migration/migration-wizard.css">
|
||||
* ...
|
||||
* </template>
|
||||
* `;
|
||||
* }
|
||||
*
|
||||
* Gets rewritten to:
|
||||
*
|
||||
* import migrationwizardStyles from "browser/themes/shared/migration/migration-wizard.css";
|
||||
* ...
|
||||
* static get markup() {
|
||||
* return`
|
||||
* <template>
|
||||
* <link rel="stylesheet" href=${migrationwizardStyles}>
|
||||
* ...
|
||||
* </template>
|
||||
* `;
|
||||
* }
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
const projectRoot = path.join(process.cwd(), "../../..");
|
||||
const rewriteChromeUri = require("./chrome-uri-utils.js");
|
||||
|
||||
/**
|
||||
* Return an array of the unique chrome:// CSS URIs referenced in this file.
|
||||
*
|
||||
* @param {string} source - The source file to scan.
|
||||
* @returns {string[]} Unique list of chrome:// CSS URIs
|
||||
*/
|
||||
function getReferencedChromeUris(source) {
|
||||
const chromeRegex = /chrome:\/\/.*?\.css/g;
|
||||
const matches = new Set();
|
||||
for (let match of source.matchAll(chromeRegex)) {
|
||||
// Add the full URI to the set of matches.
|
||||
matches.add(match[0]);
|
||||
}
|
||||
return [...matches];
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace references to chrome:// URIs with the relative path on disk from the
|
||||
* project root.
|
||||
*
|
||||
* @this {WebpackLoader} https://webpack.js.org/api/loaders/
|
||||
* @param {string} source - The source file to update.
|
||||
* @returns {string} The updated source.
|
||||
*/
|
||||
async function rewriteChromeUris(source) {
|
||||
const chromeUriToLocalPath = new Map();
|
||||
// We're going to rewrite the chrome:// URIs, find all referenced URIs.
|
||||
let chromeDependencies = getReferencedChromeUris(source);
|
||||
for (let chromeUri of chromeDependencies) {
|
||||
let localRelativePath = rewriteChromeUri(chromeUri);
|
||||
if (localRelativePath) {
|
||||
localRelativePath = localRelativePath.replaceAll("\\", "/");
|
||||
// Store the mapping to a local path for this chrome URI.
|
||||
chromeUriToLocalPath.set(chromeUri, localRelativePath);
|
||||
// Tell webpack the file being handled depends on the referenced file.
|
||||
this.addMissingDependency(path.join(projectRoot, localRelativePath));
|
||||
}
|
||||
}
|
||||
// Rewrite the source file with mapped chrome:// URIs.
|
||||
let rewrittenSource = source;
|
||||
for (let [chromeUri, localPath] of chromeUriToLocalPath.entries()) {
|
||||
// Generate an import friendly variable name for the default export from
|
||||
// the CSS file e.g. __chrome_styles_loader__moztoggleStyles.
|
||||
let cssImport = `__chrome_styles_loader__${path
|
||||
.basename(localPath, ".css")
|
||||
.replaceAll("-", "")}Styles`;
|
||||
|
||||
// MozTextLabel is a special case for now since we don't use a template.
|
||||
if (
|
||||
this.resourcePath.endsWith("/moz-label.mjs") ||
|
||||
this.resourcePath.endsWith(".js")
|
||||
) {
|
||||
rewrittenSource = rewrittenSource.replaceAll(`"${chromeUri}"`, cssImport);
|
||||
} else {
|
||||
rewrittenSource = rewrittenSource.replaceAll(
|
||||
chromeUri,
|
||||
`\$\{${cssImport}\}`
|
||||
);
|
||||
}
|
||||
|
||||
// Add a CSS import statement as the first line in the file.
|
||||
rewrittenSource =
|
||||
`import ${cssImport} from "${localPath}";\n` + rewrittenSource;
|
||||
}
|
||||
return rewrittenSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* The WebpackLoader export. Runs async since apparently that's preferred.
|
||||
*
|
||||
* @param {string} source - The source to rewrite.
|
||||
* @param {Map} sourceMap - Source map data, unused.
|
||||
* @param {Object} meta - Metadata, unused.
|
||||
*/
|
||||
module.exports = async function chromeUriLoader(source) {
|
||||
// Get a callback to tell webpack when we're done.
|
||||
const callback = this.async();
|
||||
// Rewrite the source async since that appears to be preferred (and will be
|
||||
// necessary once we support rewriting CSS/SVG/etc).
|
||||
const newSource = await rewriteChromeUris.call(this, source);
|
||||
// Give webpack the rewritten content.
|
||||
callback(null, newSource);
|
||||
};
|
|
@ -1,98 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* This file contains a webpack loader which has the goal of rewriting chrome://
|
||||
* URIs to local paths. This allows JS files loaded in Storybook to load JS
|
||||
* files using their chrome:// URI. Using the chrome:// URI is avoidable in many
|
||||
* cases, however in some cases such as importing the lit.all.mjs file from
|
||||
* browser/components/ there is no way to avoid it on the Firefox side.
|
||||
*
|
||||
* This loader depends on the `./mach storybook manifest` step to generate the
|
||||
* rewrites.js file. That file exports an object with the files we know how to
|
||||
* rewrite chrome:// URIs for.
|
||||
*
|
||||
* This loader allows code like this to work with storybook:
|
||||
*
|
||||
* import { html } from "chrome://global/content/vendor/lit.all.mjs";
|
||||
* import "chrome://global/content/elements/moz-button-group.mjs";
|
||||
*
|
||||
* In this example the file would be rewritten in the webpack bundle as:
|
||||
*
|
||||
* import { html } from "toolkit/content/widgets/vendor/lit.all.mjs";
|
||||
* import "toolkit/content/widgets/moz-button-group/moz-button-group.mjs";
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
|
||||
// Object<ChromeURI, LocalPath> - This is generated by `./mach storybook manifest`.
|
||||
const rewrites = require("./rewrites.js");
|
||||
|
||||
const projectRoot = path.join(process.cwd(), "../../..");
|
||||
|
||||
/**
|
||||
* Return an array of the unique chrome:// URIs referenced in this file.
|
||||
*
|
||||
* @param {string} source - The source file to scan.
|
||||
* @returns {string[]} Unique list of chrome:// URIs
|
||||
*/
|
||||
function getReferencedChromeUris(source) {
|
||||
// We can only rewrite files that get imported. Which means currently we only
|
||||
// support .js and .mjs. In the future we hope to rewrite .css and .svg.
|
||||
const chromeRegex = /chrome:\/\/.*?\.(js|mjs)/g;
|
||||
const matches = new Set();
|
||||
for (let match of source.matchAll(chromeRegex)) {
|
||||
// Add the full URI to the set of matches.
|
||||
matches.add(match[0]);
|
||||
}
|
||||
return [...matches];
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace references to chrome:// URIs with the relative path on disk from the
|
||||
* project root.
|
||||
*
|
||||
* @this {WebpackLoader} https://webpack.js.org/api/loaders/
|
||||
* @param {string} source - The source file to update.
|
||||
* @returns {string} The updated source.
|
||||
*/
|
||||
async function rewriteChromeUris(source) {
|
||||
const chromeUriToLocalPath = new Map();
|
||||
// We're going to rewrite the chrome:// URIs, find all referenced URIs.
|
||||
let chromeDependencies = getReferencedChromeUris(source);
|
||||
for (let chromeUri of chromeDependencies) {
|
||||
let localRelativePath = rewrites[chromeUri];
|
||||
if (localRelativePath) {
|
||||
localRelativePath = localRelativePath.replaceAll("\\", "/");
|
||||
// Store the mapping to a local path for this chrome URI.
|
||||
chromeUriToLocalPath.set(chromeUri, localRelativePath);
|
||||
// Tell webpack the file being handled depends on the referenced file.
|
||||
this.addDependency(path.join(projectRoot, localRelativePath));
|
||||
}
|
||||
}
|
||||
// Rewrite the source file with mapped chrome:// URIs.
|
||||
let rewrittenSource = source;
|
||||
for (let [chromeUri, localPath] of chromeUriToLocalPath.entries()) {
|
||||
rewrittenSource = rewrittenSource.replaceAll(chromeUri, localPath);
|
||||
}
|
||||
return rewrittenSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* The WebpackLoader export. Runs async since apparently that's preferred.
|
||||
*
|
||||
* @param {string} source - The source to rewrite.
|
||||
* @param {Map} sourceMap - Source map data, unused.
|
||||
* @param {Object} meta - Metadata, unused.
|
||||
*/
|
||||
module.exports = async function chromeUriLoader(source) {
|
||||
// Get a callback to tell webpack when we're done.
|
||||
const callback = this.async();
|
||||
// Rewrite the source async since that appears to be preferred (and will be
|
||||
// necessary once we support rewriting CSS/SVG/etc).
|
||||
const newSource = await rewriteChromeUris.call(this, source);
|
||||
// Give webpack the rewritten content.
|
||||
callback(null, newSource);
|
||||
};
|
|
@ -21,14 +21,6 @@ module.exports = {
|
|||
// Everything else
|
||||
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx|md)",
|
||||
],
|
||||
// Additions to the staticDirs might also need to get added to
|
||||
// MozXULElement.importCss in preview.mjs to enable auto-reloading.
|
||||
staticDirs: [
|
||||
`${projectRoot}/toolkit/content/widgets/`,
|
||||
`${projectRoot}/browser/themes/shared/`,
|
||||
`${projectRoot}/browser/components/firefoxview/`,
|
||||
`${projectRoot}/browser/components/aboutlogins/content/components/`,
|
||||
],
|
||||
addons: [
|
||||
"@storybook/addon-links",
|
||||
{
|
||||
|
@ -84,6 +76,28 @@ module.exports = {
|
|||
type: "asset/source",
|
||||
});
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.m?js$/,
|
||||
exclude: /.storybook/,
|
||||
use: [{ loader: path.resolve(__dirname, "./chrome-styles-loader.js") }],
|
||||
});
|
||||
|
||||
// Replace the default CSS rule with a rule to emit a separate CSS file and
|
||||
// export the URL. This allows us to rewrite the source to use CSS imports
|
||||
// via the chrome-styles-loader.
|
||||
let cssFileTest = /\.css$/.toString();
|
||||
let cssRuleIndex = config.module.rules.findIndex(
|
||||
rule => rule.test.toString() === cssFileTest
|
||||
);
|
||||
config.module.rules[cssRuleIndex] = {
|
||||
test: /\.css$/,
|
||||
exclude: [/.storybook/, /node_modules/],
|
||||
type: "asset/resource",
|
||||
generator: {
|
||||
filename: "[name].[contenthash].css",
|
||||
},
|
||||
};
|
||||
|
||||
// We're adding a rule for files matching this pattern in order to support
|
||||
// writing docs only stories in plain markdown.
|
||||
const MD_STORY_REGEX = /(stories|story)\.md$/;
|
||||
|
|
|
@ -14,22 +14,6 @@ connectFluent();
|
|||
// Any fluent imports should go through MozXULElement.insertFTLIfNeeded.
|
||||
window.MozXULElement = {
|
||||
insertFTLIfNeeded,
|
||||
|
||||
// For some reason Storybook doesn't watch the static folder. By creating a
|
||||
// method with a dynamic import we can pull the desired files into the bundle.
|
||||
async importCss(resourceName) {
|
||||
// eslint-disable-next-line no-unsanitized/method
|
||||
let file = await import(
|
||||
/* webpackInclude: /.*[\/\\].*\.css$/ */
|
||||
`browser/themes/shared/${resourceName}`
|
||||
);
|
||||
// eslint-disable-next-line no-unsanitized/method
|
||||
file = await import(
|
||||
/* webpackInclude: /.*[\/\\].*\.css$/ */
|
||||
`browser/components/firefoxview/${resourceName}`
|
||||
);
|
||||
return file;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -38,9 +38,8 @@
|
|||
commonStyles.href = "chrome://global/skin/in-content/common.css";
|
||||
const messageBarStyles = document.createElement("link");
|
||||
messageBarStyles.rel = "stylesheet";
|
||||
messageBarStyles.href = window.IS_STORYBOOK
|
||||
? "./message-bar.css"
|
||||
: "chrome://global/content/elements/message-bar.css";
|
||||
messageBarStyles.href =
|
||||
"chrome://global/content/elements/message-bar.css";
|
||||
template.content.append(commonStyles, messageBarStyles);
|
||||
|
||||
// A container for the entire message bar content,
|
||||
|
|
|
@ -13,10 +13,11 @@
|
|||
static get fragment() {
|
||||
if (!this._template) {
|
||||
let parser = new DOMParser();
|
||||
let cssPath = "chrome://global/content/elements/panel-list.css";
|
||||
let doc = parser.parseFromString(
|
||||
`
|
||||
<template>
|
||||
<link rel="stylesheet" href="chrome://global/content/elements/panel-list.css">
|
||||
<link rel="stylesheet" href=${cssPath}>
|
||||
<div class="arrow top" role="presentation"></div>
|
||||
<div class="list" role="presentation">
|
||||
<slot></slot>
|
||||
|
@ -31,11 +32,7 @@
|
|||
true
|
||||
);
|
||||
}
|
||||
let frag = this._template.content.cloneNode(true);
|
||||
if (window.IS_STORYBOOK) {
|
||||
frag.querySelector("link").href = "./panel-list/panel-list.css";
|
||||
}
|
||||
return frag;
|
||||
return this._template.content.cloneNode(true);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
|
@ -512,9 +509,7 @@
|
|||
|
||||
let style = document.createElement("link");
|
||||
style.rel = "stylesheet";
|
||||
style.href = window.IS_STORYBOOK
|
||||
? "./panel-list/panel-item.css"
|
||||
: "chrome://global/content/elements/panel-item.css";
|
||||
style.href = "chrome://global/content/elements/panel-item.css";
|
||||
|
||||
this.button = document.createElement("button");
|
||||
this.button.setAttribute("role", "menuitem");
|
||||
|
|
Загрузка…
Ссылка в новой задаче