How to use a custom Webpack Loader in Gatsby JS? - node.js

I am trying to change the default webpack loader for SVG files in Gatsby JS. I want to use the 'svg-url-loader' instead of the default 'url-loader'. I have installed it, and it works fine with webpack-inline-loaders.
But to avoid repeating the process, I decided to use the onCreateWebpackConfig Node API to change the loader for the SVG files. So I added the below code in the gatsby-node.js file.
exports.onCreateWebpackConfig = ({
stage,
getConfig,
rules,
loaders,
plugins,
actions,
}) => {
actions.setWebpackConfig({
module: {
rules: [
{
test: /\.svg/,
use: {
loader: "svg-url-loader",
options: {
limit: 4096,
iesafe: true,
},
},
},
],
},
});
};
But the website is now not displaying any SVG image but alt text. This is because the src attributes of those IMG tags are using a faulty base64 encoded image and not the UTF8 encoded SVG XML tag.
The console is not logging any errors. I have also tried creating a local plugin at the /plugins dir, but it doesn't work. I am developing my site on my local machine and building it using Gatsby Cloud. The problem exists in both places.
Here's the link to a minimal repro.

The problem here is that to use a custom loader in any framework, you have to disable the default loader first. Then you'll be able to add your custom loader. So, first, you have to disable the default, 'url-loader' and then set the 'svg-url-loader' for SVG files.
exports.onCreateWebpackConfig = ({
stage,
getConfig,
rules,
loaders,
plugins,
actions,
}) => {
const config = getConfig();
config.module.rules.find(
(rule) =>
rule.test &&
rule.test.toString() ===
"/\\.(ico|svg|jpg|jpeg|png|gif|webp|avif)(\\?.*)?$/"
).test = /\.(ico|jpg|jpeg|png|gif|webp|avif)(\?.*)?$/;
config.module.rules.push({
test: /\.svg/,
use: {
loader: "svg-url-loader",
options: {
limit: 4096,
iesafe: true,
},
},
});
actions.replaceWebpackConfig(config);
};

Related

Minimal monaco-editor without language support

I am trying to configure Monaco as a webcomponent (using vite's lit-ts template) that edits a single, custom language and without lots of Monaco's extra features. (I then want to copy the output to include using a script tag in my main project's index.html)
In my main file I have
import {monaco} from "./customMonaco";
// add support for the 1 custom language I do want
import "./mix.contribution.js";
In customMonaco.ts I have
import "monaco-editor/esm/vs/editor/browser/viewParts/contentWidgets/contentWidgets.js";
import "monaco-editor/esm/vs/editor/browser/viewParts/lineNumbers/lineNumbers.js";
import "monaco-editor/esm/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css";
import "monaco-editor/esm/vs/editor/browser/viewParts/linesDecorations/linesDecorations.js";
import "monaco-editor/esm/vs/editor/browser/viewParts/linesDecorations/linesDecorations.css";
import * as monaco from "monaco-editor/esm/vs/editor/editor.api.js";
export {monaco};
But when I build this I am still getting all the language files.
My vite config is
export default defineConfig({
build: {
lib: {
entry: 'src/rmx-monaco.ts',
formats: ['es']
},
rollupOptions: {
external: /^lit/,
// output: {
// manualChunks: undefined,
// inlineDynamicImports: true,
// entryFileNames: "[name].js",
// // Prevent vendor.js being created
// // prevents adding cache busting
// chunkFileNames: "assets/[name].js",
// assetFileNames: "rmx-monaco.[ext]",
// },
}
}, server: {port: 5000}
})
and next I will uncomment the output section so as to get just 1 js file, but I don't want it to include language support

how to add a node import statement to a file with angular schematics?

I want a custom schematic that will create a page in my application. To do this, I want to use the existing core schematics to create a routing module, then create a component in that module to display my page content, and finally wire everything together. It's the "wire everything together" that has me stumped. I can't find clear guidance on the "right" way to to this. Should I:
a) directly modify the existing files that the "module" and "component" schematics created? (I don't know how to do that)
b) use templates to somehow merge the modifications into the files? (I don't know how to do this either)
Below is the index.ts file for my page schematic as of now. What I don't know how to do is commented by "TODO" towards the end.
Please help!
import {
externalSchematic,
Rule,
SchematicContext,
Tree,
chain,
} from '#angular-devkit/schematics';
export function page(options: any): Rule {
const name = options.name;
return chain([
// create module for this page
externalSchematic('#schematics/angular', 'module', {
name: `pages/${name}`,
routing: true
}),
// create simple component to display in this page
externalSchematic('#schematics/angular', 'component', {
name: `pages/${name}/${name}`,
routing: true
}),
// add component to routing module
(tree: Tree, _context: SchematicContext) => {
// TODO 1: import new component into routing module
// e.g. import { MyPageComponent } from "./my-page/my-page.component";
// TODO 2: add component to routes
// const routes: Routes = [{ path: '', pathMatch: 'full', component: MyPageComponent }];
return tree;
},
]);
}

Webpack Config - Pass [hash] to loader data

Im using the pug-html-loader to load a large number of pug templates and convert them to html in the build directory. this is working fine, but I want to be able to insert the webpack hash in the pug templates.
I cant work out how to set up my webpack.config to pass this data to the pug-html-loader
This is the loader in my webpack.config
{
loader: "pug-html-loader",
options: {
exports: false,
pretty: PRODUCTION ? false : true,
data: { hash: WEBPACK HASH HERE }
}
}
As you can see I want to insert the webpack hash in the data object where I have WEBPACK HASH HERE currently, but cant work out how to access/pass that in the webpack.config file

How to run intern to test a dojo application running with node.js ?

I'm trying to use intern to test a dojo application running under node.js
My intern.js configuration file is something like:
define({
loader: {
packages: [
{ name: 'elenajs', location: 'lib' },
{ name: 'tests', location: 'tests' }
],
map: {
'elenajs': { dojo: 'node_modules/dojo' },
'tests': { dojo: 'node_modules/dojo' }
}
},
suites: [ 'tests/all' ]});
When I try to run a test with node node_modules/intern/client.js config=tests/intern, I get this error:
Error: node plugin failed to load because environment is not Node.js.
Normally I would have configured dojo with something like
dojoConfig = {
...
hasCache: {
"host-node": 1, // Ensure we "force" the loader into Node.js mode
"dom": 0 // Ensure that none of the code assumes we have a DOM
},
...
};
How can I solve this with intern?
The issue that you are experiencing is caused by the fact that there is code in Dojo that relies on certain has-rules being set from the Dojo loader, but this doesn’t happen because the Dojo loader isn’t in use. There are a couple of potential workarounds:
Since Intern’s loader sets some of the same rules, you can load intern/node_modules/dojo/has and run has.add('dojo-has-api', true) before anything else tries to load your dojo/has module. This should cause Dojo to use the has.js implementation from Intern’s loader (and adopt all the rules it already has set, which currently includes host-node). The best place to do this is going to be the Intern configuration file, which would end up being something like this:
define([ 'intern/dojo/has' ], function (has) {
has.add('dojo-has-api', true);
return {
// your existing Intern configuration object…
};
});
Before loading any modules that call has('host-node'), load dojo/has and intern/node_modules/dojo/has and call dojoHas.add('host-node', internHas('host-node')) (or I guess you can just hard code it :)). This would require a loader plugin to be used in place of your suites array:
// tests/preload.js
define({
load: function (id, require, callback) {
require([ 'dojo/has' ], function (has) {
has.add('host-node', true);
require(id.split(/\s*,\s*/), callback);
}
}
});
And then your suites would change to suites: [ 'tests/preload!tests/all,tests/foo,tests/bar' ].
Any other has-rules that Dojo code relies on that are set by the Dojo loader will need to be set yourself. Any other rules that Dojo adds from other parts of itself will work correctly.

Is there a more concise way of including dependencies in RequireJS

Suppose I have a module that starts like the following:
define(['jquery', 'actions', 'util', 'text!../templates/dialog.html!strip', 'text!../templates/requestRow.html!strip', 'text!../templates/respondForm.html!strip'], function($, actions, util, tDialog, tRequestRow, tRespondForm) {
This module contains most of the code for writing to my client UI. It also loads a couple other modules I've written as well as 3 HTML templates using the text.js plugin. I'm wondering if there's a more concise way of doing this? As the application grows, I may have additional templates to load, or modules and it just seems like my define statement could grow to be a bit ugly. Should I just add my template paths to require.config in my main.js like this:
require.config({
baseUrl: '/webrequests/resources/scripts/',
paths: {
'modernizr': '../js/vendor/modernizr-2.6.2-respond-1.1.0.min',
'bootstrap' : '../js/vendor/bootstrap.min',
'dialog' : 'text!../templates/dialog.html!strip',
'requestRow' : 'test!../templates/requestRow.html!strip',
'respondForm' : 'text!../templates/respondForm.html!strip'
}});
Is there perhaps some way to load all templates within a directory and just have 1 dependency to include in the define statement?
Thanks in advance.
You could make a module for loading in the templates you frequently use. So group the templates to be loaded in one module, that way you can just load this template module instead of the individual templates:
// generalTemplates.js
define([
'text!../templates/dialog.html!strip',
'text!../templates/requestRow.html!strip',
'text!../templates/respondForm.html!strip'
], function (tDialog, tRequestRow, tRespondForm) {
return {
dialog: tDialog,
requestRow: tRequestRow,
respondForm: tRespondForm
};
});
So that in your module, you can simply include the templates like any other module:
define([
'jquery',
'actions',
'util',
'generalTemplates'
], function($, actions, util, templates) {
var tDialog = templates.dialog,
tRequestRow = templates.requestRow,
tRespondForm = templates.respondForm;
/* You can do stuff with the templates here */
});

Resources