I'm using tightenco/jigsaw to generate static HTML, I have a couple of Vue app, which is compiled by laravel-mix. I want to generate HTML for the different languages I'm able to translate the blade templates but for the Vue part I don't want to load the translations in runtime. My plan is to interact with the vue-loader and vue-template-compiler on the compiler method, I tried these approaches but none of them are working.
// addon.js
const mix = require("laravel-mix");
class CustomVue {
webpackRules() {
return {
test: /\.vue$/,
loaders: [
{
loader: "vue-loader",
options: {
compiler: require("vue-template-compiler"),
compile: function (template, options) {
console.log("++++++++++++++++++++++++");
console.log(template);
console.log("++++++++++++++++++++++++");
// Modify the template code here
return template;
}
}
}
]
};
}
}
mix.extend("translatedVue", new CustomVue());
and
// webpack.mix.js
mix
.jigsaw()
.translatedVue()
.js("source/_assets/js/main.js", "js")
.sass("source/_assets/scss/main.scss", "css")
.sass("source/_assets/scss/cf-errors/main.scss", "css/cf-errors/")
With the above approach, nothing is logging in to the console. I also tried like this
const customDirective = () => {
return {
name: "v-html",
postTransformNode(el) {
console.log(el);
}
};
};
mix.config.vue = {
compilerOptions: {
// add the custom directive to the list of plugins
plugins: [customDirective()]
}
};
To see if it's logging something, but no luck.
How can I interact with the template SFC before getting compiled?
Related
So I'm trying to use a custom webpack config in my Angular 10.x app where I want to remove 'data-test' attributes from my templates during compilation, so the production code does not contain any e2e selector references. For this, I'm using the custom webpack builder (https://www.npmjs.com/package/#angular-builders/custom-webpack) with a custom webpack config. I'm loading the config in the angular.json like this:
"builder": "#angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.ts",
"mergeStrategies": {
"externals": "append"
}
},
And the webpack config looks like this:
import { CustomWebpackBrowserSchema, TargetOptions } from '#angular-builders/custom-webpack';
import { EnvironmentType, getEnumValues } from '#enum-package/core/enumeration';
import * as webpack from 'webpack';
export default (config: webpack.Configuration, options: CustomWebpackBrowserSchema, targetOptions: TargetOptions) => {
const env: EnvironmentType =
<EnvironmentType>(
getEnumValues(EnvironmentType).find(environmentType =>
targetOptions.configuration?.toLowerCase().includes(environmentType.toLowerCase()),
)
) ?? EnvironmentType.DEVELOPMENT;
if (config.module?.rules) {
// Remove E2E testing attributes from production code
if (env === EnvironmentType.PRODUCTION) {
const testSelectorRegex = new RegExp(/data-test="(.*)"|#HostBinding\('attr.data-test'\)(.*);/g);
config.module.rules.push({
test: /\.(js|ts|html)$/,
enforce: 'pre',
loader: 'string-replace-loader',
options: {
search: testSelectorRegex.source,
replace: match => {
console.log(`Replace E2E selector '${match}'.`);
return ' ';
},
flags: 'g',
},
});
}
}
return config;
};
The search-replace-loader package (https://www.npmjs.com/package/string-replace-loader) is what takes care of replacing the actual attributes from the templates. While running the ng build command, I can actually see that the replace itself works, since the Replace E2E selector '${match}'. is actually running and I can see the tags that I want to remove being logged during compilation.
For some reason when running the app from the dist folder after compilation, the tags are still in place when I inspect the DOM in my browser.
Am I missing something? Is there a build step before or after running this webpack loader that does not use the replaced source code? Does this have anything to do with the Ivy build engine that we're using?
I have a script which generates a file, call it auto.js. This file contains some dynamically generated imports and is being used within a VueJS project.
// auto.js
import { apple, strawberry, orange } from 'delicious-fruits';
import { carrot, cucumber, celery } from 'delicious-vegetables';
While using Webpacks dev server, should any project file change, my goal is to have this script re-generate my auto.js file, and then have that included in the re-compiled project.
I have turned this script into a Webpack plugin, whereby I'm listening for the watchRun compiler hook. This seems like the ideal hook, per its description:
Executes a plugin during watch mode after a new compilation is triggered but before the compilation is actually started.
class AutoGenerate {
constructor(options) {
this.options = options;
}
apply(compiler) {
compiler.hooks.watchRun.tap('AutoGenerate', () => {
generateFile()
})
}
}
function generateFile () {
// generate auto.js and write to disk
}
I always wind up with an infinite loop situation. I have tried approaching the problem by using various life cycles events (hooks), as well ignoring the auto-generated file. Of course, by ignoring it, those changes are not included in the re-compiled project.
const webpack = require('webpack');
const AutoGenerate = require("./auto.plugin");
module.exports = {
configureWebpack: {
plugins: [
new webpack.WatchIgnorePlugin([/.*auto\.js/]),
new AutoGenerate()
]
}
}
I've also tried tapping into the compilation, and adding a new asset to the compilation. While the process does not error out, the generated asset is not a part of the final compilation.
// auto.plugin.js
class AutoGenerate {
static defaultOptions = {
outputFile: 'auto.js',
};
constructor(options = {}) {
this.options = { ...AutoGenerate.defaultOptions, ...options };
}
apply(compiler) {
compiler.hooks.thisCompilation.tap('AutoGenerate', (compilation) => {
const path = require("path")
const filePath = path.resolve(__dirname, `src/plugins/${this.options.outputFile}`)
const { RawSource } = require('webpack-sources')
const fileContent = new RawSource(generateFile())
compilation.emitAsset(
filePath,
fileContent
)
});
}
}
function generateFile() {
// generate file content & return as string
}
module.exports = { AutoGenerate };
// vue.config.js
const AutoGenerate = require("./auto.plugin");
module.exports = {
configureWebpack: {
plugins: [
new AutoGenerate()
]
}
}
How can I trigger my logic for auto-generating this file, while having this file be included as part of any re-compilation, while at the same time avoiding an infinite loop?
I have not been able to identify a direct solution to the problem posed above. However, for anyone reading, I've come to discover that this can be accomplished by utilizing a package called before-build-webpack, notably by including the watch-run trigger.
// vue.config.js
const WebpackBeforeBuildPlugin = require('before-build-webpack')
module.exports = {
configureWebpack: {
plugins: [
new WebpackBeforeBuildPlugin(function(stats, callback) {
// ...
}, ['run', 'watch-run'])
]
}
}
I'm am using TS + CSS + SCSS in nextJS. I will import some CSS files, but I want to set cssModule:false to those CSS files, then I will import my own SCSS files and set cssModule:true.
The below is my code in next.config.js, it transfers CSS files to module.
const withSass = require("#zeit/next-sass");
const withTypescript = require("#zeit/next-typescript");
const withCSS = require("#zeit/next-css");
module.exports = withTypescript(
withCSS(
withSass({
cssModules: true
})
)
);
Could you please advise me on the right approach of importing CSS files?
The configuration of next-css is global, you either using cssModules or not.
My solution for this was to configure webpack manually to not apply cssModules on files with .global.css suffix.
config.module.rules.forEach(rule => {
if (rule.test.toString().includes('.scss')) {
rule.rules = rule.use.map(useRule => {
if (typeof useRule === 'string') {
return {
loader: useRule,
};
}
if (useRule.loader.startsWith('css-loader')) {
return {
oneOf: [
{
test: /\.global\.scss$/,
loader: useRule.loader,
options: {
...useRule.options,
modules: false,
},
},
{
loader: useRule.loader,
options: useRule.options,
},
],
};
}
return useRule;
});
delete rule.use;
}
});
There is an open PR for next-css to include similar solution to the lib.
I'm writing a webpack loader for a specific file extension, and I would like to append a custom import to the DOM
module.exports = function(source) {
...
document.appendChild(myImport)
return `export default 'hello'`;
}
but the DOM is not accessible
ReferenceError: document is not defined
Is there a way to access the DOM from my loader?
My webpack configuration is:
const path = require('path')
module.exports = {
...
module: {
rules: [{
test: /\.myextension$/,
use: {
loader: 'my-loader'
}
}]
}
};
No, the code from loader is going to be ran on a node.js env, which has no power over the dom (browser env). To be able to manipulate the dom, you would have to output a code though loader which would be inserted on that type of file and then when executed on the browser it would do the modifications.
Something like:
module.exports = function(source) {
return `export default function(){
document.appendChild(myImport)
}`;
}
In my SCSS file I need to use different base urls for different app environments which will be prepended to the image name.
Example:
For production environment
background: url(/prod/image.png);
For development environment
background: url(/dev/image.png);
The helper function which I'm using in the rest of my app returns the base path of the static assets and it looks like this:
static imagePath() {
let imagesPath;
if (this.isProduction()) {
basePath = '/prod';
} else {
basePath = '/dev';
}
return basePath
}
How to achieve this?
Edit:*
I'm using extract-text-webpack-plugin which won't let me output multiple css files.
For example you can have 2 main files (dev.scss and prod.scss) that will look like:
// prod.scss
$basePath: '/prod';
#import "style.scss";
and same for dev.scss.
Otherwise you can use some placeholder for path prefix and substitute it with actual prefix on post-processing step. For example you can use this plugin for PostCSS.
UPDATE:
Following discussion in comments here is (untested) example of how webpack configuration may look like:
module.exports = {
// ....
module: {
rules: [
// ....
{
test: /\.scss$/,
use: {
loader: StringReplacePlugin.replace({
replacements: [
{
pattern: /{urlPrefix}/ig,
replacement: () => process.env.NODE_ENV !== 'production' ? '/dev' : '/prod',
}
]
}, 'sass-loader'),
}
},
// ....
],
},
plugins: [
new StringReplacePlugin(),
// ....
],
// ....
};