Rename webpack/gatsby chunkmapping strings - javascript

When building our production app in Gatsby, I see something like this:
window.___chunkMapping={
"app":[],
"component---src-templates-page-tsx":[],
"component---src-templates-pages-newsletter-tsx":[]
}
Is it possible to hash these paths instead of printing them out? We don‘t want to expose too much from what is happening in the back.
I tried setting these configs in webpack:
output: {
filename: `[chunkhash:2][contenthash:5].js`,
chunkFilename: `[chunkhash:2][contenthash:5].js`,
},
And it successfully hashes .js files but not the template paths.

I upvoted this question when I first saw it, I think it's definitely should be done in production build.
Unfortunately, componentChunkName (the template path in question) is generated by Gatsby in createPage & not handled by webpack.
The code that generates componentChunkName is over here: github
I tried to modify the code as follow:
const { kebabCase } = require(`lodash`)
const path = require(`path`)
+ const uuidv5 = require(`uuid/v5`)
const { store } = require(`../redux`)
const generateComponentChunkName = componentPath => {
const program = store.getState().program
let directory = `/`
if (program && program.directory) {
directory = program.directory
}
const name = path.relative(directory, componentPath)
- return `component---${kebabCase(name)}`
+ return process.env.NODE_ENV === `production`
+ ? `component---${uuidv5(name, uuidv5.URL)}`
+ : `component---${kebabCase(name)}`
}
exports.generateComponentChunkName = generateComponentChunkName
This successfully hides all the component names in production build:
app: Array [ "/app-e593b3d93932ed3a0363.js" ]
"component---11d478fe-6a55-579c-becf-625ab1e57cf4": Array [ "/component---11d478fe-6a55-579c-becf-625ab1e57cf4-76c90ae50035c52657a0.js" ]
"component---15c76861-b723-5e0a-823c-b6832aeeb0a0": Array [ "/component---15c76861-b723-5e0a-823c-b6832aeeb0a0-18eb457ba6c147e1b31b.js" ]
...
None of the local unit tests failed, my clicking-around-until-something-breaks test also hasn't yielded any errors. I might submit a PR later today to see if the maintainers have some insights on why this is not a good idea.
Edit: I opened an issue instead: github, you can subscribe to the issue to see how it resolves.

Related

How can i get a flat list of file dependencies filtered by entry point from the compilation in webpack 5

I'm trying to update the svg-chunk-webpack-plugin from webpack v4 to v5. The plugin used the compilation object and needs a flat list of file dependencies filtered by entry point to extract all SVGs for each entry points.
Example of a nested dependency tree with an entry point app-a
- app-a.js
- svg1.svg
- module1.js
- svg2.svg
- svg3.svg
- module2.js
- svg4.svg
- svg5.svg
With webpack v4, the following code works and returns an array of all SVG files used by each entry points
const path = require('path');
function getSvgsDependenciesByEntrypoint(entryName) {
let listSvgs = [];
compilation.entrypoints.get(entryName).chunks.forEach((chunk) => {
chunk.getModules().forEach((module) => {
module.buildInfo &&
module.buildInfo.fileDependencies &&
module.buildInfo.fileDependencies.forEach((filepath) => {
const extension = path.extname(filepath).substr(1);
if (extension === 'svg') {
listSvgs.push(filepath);
}
});
});
});
return listSvgs;
}
const svgs = getSvgsDependenciesByEntrypoint('app-a');
console.log(svgs) // ["svg1.svg", "svg2.svg", "svg3.svg", "svg4.svg", "svg5.svg"]
With webpack v5, I've tried the following code which produce different results between development and production build.
const path = require('path');
function getSvgsDependenciesByEntrypoint(entryName) {
let listSvgsDependencies = [];
compilation.entrypoints.get(entryName).chunks.forEach((chunk) => {
compilation.chunkGraph.getChunkModules(chunk).forEach((module) => {
module.dependencies.forEach((dependency) => {
const extension = path.extname(dependency.userRequest).substr(1);
if (extension === 'svg') {
const moduleDependency = compilation.moduleGraph.getModule(dependency);
listSvgsDependencies.push(moduleDependency);
}
});
});
});
return listSvgsDependencies;
}
In development build
const svgs = getSvgsDependenciesByEntrypoint('app-a');
console.log(svgs) // ["svg1.svg", "svg2.svg", "svg3.svg", "svg4.svg", "svg5.svg"]
In production build
const svgs = getSvgsDependenciesByEntrypoint('app-a');
console.log(svgs) // ["svg1.svg"]
Nested dependencies are not find with the production build
I fixed the bug with the webpack team on issue #12202.
This behavior is related to the side effects of the module. The webpack release v5.10.3 includes a fix which allows to manually specify the module has side effects. You could do that either in module rules or within the loader directly.

Issue handling file download from cypress

While I was working on Cypress trying to download a .xlsx report and further manipulate the data in it for further verification, problem I faced was when Cypress was running test with the electron browser-it prompted a window based popup.
Moreover, when i selected chrome browser for running tests, the default directory of download directory could not be modified. Hence, manipulation of data wasn't possible if it's not present in the project directory as it would cause faliures in the CI execution...
Any workaround for this would be appreciated.
I solved it with the index.js file in the plugins folder by doing the following stuff:
const cypressTypeScriptPreprocessor = require('./cy-ts-preprocessor');
const path = require('path');
const fs = require('fs');
const RESULT_FOLDER = 'results';
const downloadDirectory = path.join(__dirname, '..', RESULT_FOLDER);
module.exports = on => {
on('file:preprocessor', cypressTypeScriptPreprocessor);
on('before:browser:launch', (browser = {}, options) => {
if (fs.existsSync(downloadDirectory)) {
fs.rmdirSync(downloadDirectory, { recursive: true });
}
if (browser.family === 'chromium' && browser.name !== 'electron') {
options.preferences.default['download'] = { default_directory: downloadDirectory };
return options;
}
if (browser.family === 'firefox') {
options.preferences['browser.download.dir'] = downloadDirectory;
options.preferences['browser.download.folderList'] = 2;
return options;
}
});
};
The documentation for that you will find here: https://docs.cypress.io/api/plugins/browser-launch-api.html#Change-download-directory
Be aware this works for Chromium browsers but currently not for the Electron browser in CI mode. Cypress knows about the issue and is currently implementing a solution for that: https://github.com/cypress-io/cypress/issues/949#issuecomment-755975882
You can change the download path in your test like below.
const downloadFolder = path.resolve(__dirname, '../users/user/source/repos/containingRoot/cypress/downloads');

How to configure jest snapshot locations

I'd like for my jest snapshots to be created as a sibling to my test file
I current have my snapshots being put in the default __snapshots__ folder.
Current:
What I would like to achieve:
I found this post on github: https://github.com/facebook/jest/issues/1650
In the thread someone says the following should work but I haven't had any luck (even with changing the regex and other changes):
module.exports = {
testPathForConsistencyCheck: 'some/example.test.js',
resolveSnapshotPath: (testPath, snapshotExtension) =>
testPath.replace(/\.test\.([tj]sx?)/, `${snapshotExtension}.$1`),
resolveTestPath: (snapshotFilePath, snapshotExtension) =>
snapshotFilePath.replace(snapshotExtension, '.test'),
}
In package.json (or if you use jest.config.js) you need to add the path for the snapshotResolver file:
"jest": {
"snapshotResolver": "./snapshotResolver.js"
}
snapshotResolver.js is a file with code that you found in a Github issue.
In my case this file was located at the root of the project (near node_modules folder)
These solutions are more complicated that is needed for what you are trying to do.
As per pointed out in https://github.com/facebook/jest/issues/1650
Solution
Create a file: I used - 'jest/snapshotResolver.js'
module.exports = {
resolveSnapshotPath: (testPath, snapshotExtension) =>
testPath + snapshotExtension,
resolveTestPath: (snapshotFilePath, snapshotExtension) =>
snapshotFilePath.replace(snapshotExtension, ''),
testPathForConsistencyCheck: 'some.test.js',
};
in your jest config set that file to the resolver
snapshotResolver: './jest/snapshotResolve.js',
or if your jest config in package.json:
"snapshotResolver": "./jest/snapshotResolve.js",
Explanation
In short these two functions mirror each other, one takes the test file path and returns the snapshot path, the other takes the snapshot path and returns the test file path. The third is a file path example for validation.
Clearer code To help clarify what is going on
One thing to keep in mind is that the path is the full path not the relative path.
/**
*
* #param testPath Path of the test file being test3ed
* #param snapshotExtension The extension for snapshots (.snap usually)
*/
const resolveSnapshotPath = (testPath, snapshotExtension) => {
const snapshotFilePath = testPath + snapshotExtension; //(i.e. some.test.js + '.snap')
return snapshotFilePath;
}
/**
*
* #param snapshotFilePath The filename of the snapshot (i.e. some.test.js.snap)
* #param snapshotExtension The extension for snapshots (.snap)
*/
const resolveTestPath = (snapshotFilePath, snapshotExtension) => {
const testPath = snapshotFilePath.replace(snapshotExtension, '').replace('__snapshots__/', ''); //Remove the .snap
return testPath;
}
/* Used to validate resolveTestPath(resolveSnapshotPath( {this} )) */
const testPathForConsistencyCheck = 'some.test.js';
module.exports = {
resolveSnapshotPath, resolveTestPath, testPathForConsistencyCheck
};
Also, in addition to the path to the snapshotResolver as suggested in the Vasyl Nahuliak's answer, to achieve having the snapshot and the test files looking like this:
file.test.js
file.test.js.snap
your snapshotResolver should look like this:
module.exports = {
testPathForConsistencyCheck: 'some/example.test.js',
resolveSnapshotPath: (testPath, snapshotExtension) =>
testPath.replace(/\.test\.([tj]sx?)/, `.test.$1${snapshotExtension}`),
resolveTestPath: (snapshotFilePath, snapshotExtension) =>
snapshotFilePath.replace(snapshotExtension, ''),
};

How to sync a local folder with gulp, with update for deleted files and folders?

I am working on a WordPress plugin and have all the files in my working directory and run gulp in that project folder. Now, I'd like to have a watch task that copies all the changes to my local WP installation for testing.
Therefore I am looking for a way to sync (only in one direction) the project folder with the plugin folder of WP.
I managed to get it to work with gulp-directory-sync
...
var dirSync = require("gulp-directory-sync");
var localDir = "../newDir/";
var buildDir = "./buildDir/";
...
function copy_to_local_folder() {
return pipeline(
gulp.src(buildDir+'**/*'),
dirSync( buildDir, localDir, { printSummary: true } )
);
}
function watch_local() {
gulp.watch(buildDir+'**/*', copy_to_local_folder);
exports.default = watch_local;
However, the plugin hasn't been updated in 4 years and according to this answer, it is not doing it the proper "gulp way" (e.g. not using gulp-src) and this task should be possible with other basic gulp functions.
Copying changed files is pretty easy, but also keeping track of deleted files is more complicated. I also would prefer to only update changed/deleted/new files and not clearing the folder every time before coping all files.
Starting with the updated code in the aforementioned answer, I tried to implement it and made changes to make it work.
...
var newer = require("gulp-newer");
var pipeline = require("readable-stream").pipeline;
var del = require("del");
var localDir = "../newDir/";
var buildDir = "./buildDir/";
function copy_to_local_folder() {
return pipeline(
gulp.src([buildDir+'**/*']),
newer(localDir),
gulp.dest(localDir),
);
}
function watch_local() {
var watcher = gulp.watch(buildDir + '**/*', copy_to_local_folder );
watcher.on('unlink', function(path) {
console.log(path);
var newPath = './'+path;
newPath = newPath.replace(buildDir, localDir);
console.log(newPath);
(async () => {
const deletedPaths = await del(newPath, {dryRun: true, force: true});
console.log('Deleted files and directories:\n', deletedPaths.join('\n'));
})();
});
}
exports.default = watch_local;
With this code, the folder gets updated when I change or delete files, but it does not trigger when I delete an entire folder. Which is probably because I use unlink and not unlinkDir. But even if I use the version of the function below, it doesn't get triggered by deleting a folder (with containing files).
watcher.on('unlinkDir', function(path) {
console.log('folder deleted');
console.log(path);
var newPath = './'+path;
newPath = newPath.replace(buildDir, localDir);
console.log(newPath);
});
What am I doing wrong?
Or is there in general a better way to achieve this?
PS: I'm using
node v11.15.0
gulp v4.0.2
on Linux
deleting files and folders in VS Code
Update:
When I run it with:
watcher.on('unlink', ... and delete a file:
it works
with the console.log output and the ( async () => ...
and Starting and Finished for copy_to_local_folder
watcher.on('unlinkDir', ... and delete a folder:
it works not
nothing happens in the console output
(not even Starting)
watcher.on('unlinkDir', ... and delete a file:
Starting and Finished for copy_to_local_folder
but not the console.log and ( async () => ...
watcher.on('add', ... and watcher.on('addDir', ...
work both
Seems to me that the watcher.on('unlinkDir', ... does never get triggered ... is unlinkDir not supported by gulp-watch?

How can I automatically update my javascript file to import newly detected files?

I tagged watchman as it MIGHT be the solution I'm looking for, but I don't quite know how to use it in this way!
I have a directory
/imgs
/icons
/bird.png
/cat.png
/dog.png
/pig.png
and I have a file
/imgs/index.js
My index.js is responsible for importing all of the images and then exporting a single object for the rest of my project to use.
const bird = require('./icons/bird.png');
const cat = require('./icons/cat.png');
const dog = require('./icons/dog.png');
const pig = require('./icons/pig.png');
const Icons = { bird, cat, dog, pig };
export default Icons;
What I want to do is watch my imgs folder for new additions, and automatically update my index.js to import these new files and add them to my object. I need to import them with Common.js and export them with ES6.
Does anyone know a good solution to this problem?
A potential solution is to write a JavaScript script that generates your index.js like so:
'use strict';
const fs = require('fs');
const DIR = __dirname + '/imgs/icons';
const output = __dirname + '/imgs/index.js';
return fs.readdir(DIR, (err, files) => {
let result = '';
let references = [];
files.forEach(item => {
// assuming item has the format animal.png
let newReference = item.split('.')[0];
references.push(newReference);
result += `const ${newReference} = require('./icons/${item}');\n`;
});
result += `\nconst Icons = { ${references} };\n\nexport default Icons;`;
fs.writeFile(output, result, err => {
if (err) throw err;
console.log(output + ' updated');
});
});
Place that file (let's call it watcher.js for this purpose) in imgs's parent directory and make watchman run it whenever changes in your icons directory are detected:
watchman imgs/icons "node watcher.js"
Notice that if a new file gets put into the watched directory, the index.js-creating script will not re-run. Only if it gets altered again (even if just gets saved again with the same data), the index.js will reflect that change.
You can simply test that by running touch imgs/icons/crabigator.png twice and look at the watchman log and/or content of index.js.

Categories