Skip to content

Commit

Permalink
Merge pull request #5 from VladimirMikulic/fix/development-mode
Browse files Browse the repository at this point in the history
Fix development mode.
  • Loading branch information
VladimirMikulic committed Mar 28, 2020
2 parents 1e2e935 + 761a07d commit a85d81c
Show file tree
Hide file tree
Showing 15 changed files with 416 additions and 38 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@
"prefer-destructuring": "off",
"no-param-reassign": "warn"
},
"ignorePatterns": ["dist", "test"]
"ignorePatterns": ["dist*", "test"]
}
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist
dist*
.cache
node_modules
coverage
node_modules
8 changes: 7 additions & 1 deletion lib/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ class Config {
rootDir,
publicURL,
production,
mode: process.env.NODE_ENV,
_pluginConfig: getPluginConfiguration()
});
}

get mode() {
if (this.production) {
return 'production';
}
return 'development';
}

get pluginFolderConfig() {
return this._pluginConfig.config || {};
}
Expand Down
80 changes: 54 additions & 26 deletions lib/FileProcessor.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const path = require('path');
const fs = require('fs');
const glob = require('glob');
const {
createFolderSync,
replaceInFileSync,
isMapFile,
getFileNameFromPath,
moveFileSync,
isStyleJSFile
moveFileSync
} = require('./Utils');

class FileProcessor {
Expand All @@ -21,20 +21,15 @@ class FileProcessor {
processFile() {
const { oldFilePath, newFilePath, folderPath } = this;

// If the file that is not in the graph was provided (like .DS_Store)
if (!this.depGraph.hasNode(oldFilePath)) return;
if (!fs.existsSync(oldFilePath)) return;

createFolderSync(folderPath);
moveFileSync(oldFilePath, folderPath);

if (process.env.updateFileReferences === 'false') return;

if (isMapFile(oldFilePath)) {
this.updateSourceRootPathInFile();
this.updateNodeModulesPathInFile();
}

if (isStyleJSFile(oldFilePath)) {
this.updateNodeModulesPathInFile();
}

this.updateDependecies();
Expand All @@ -53,26 +48,21 @@ class FileProcessor {
replaceInFileSync(newFilePath, oldSourceRootPath, newSourceRootPath);
}

updateNodeModulesPathInFile() {
const { rootDir } = this.config;
const { newFilePath, folderPath } = this;

const CWD = process.cwd();
const nodeModulesPath = path.join(CWD, 'node_modules');

const oldNodeModulesPath = path.relative(rootDir, nodeModulesPath);
const newNodeModulesPath = path.relative(folderPath, nodeModulesPath);

replaceInFileSync(newFilePath, oldNodeModulesPath, newNodeModulesPath);
}

updateDependecies() {
const { publicURL, outDir } = this.config;
const { oldFilePath, newFilePath, folderPath } = this;

// If the file that is not in the graph was provided (like .DS_Store)
if (!this.depGraph.hasNode(oldFilePath)) return;
const fileDependeciesPaths = this.depGraph.dependenciesOf(oldFilePath);

fileDependeciesPaths.forEach(fileDepPath => {
fileDepPath = this.depGraph.getNodeData(fileDepPath);
fileDependeciesPaths.forEach(depPath => {
// fileDepPath = this.depGraph.getNodeData(fileDepPath);
let fileDepPath = getFileNameFromPath(depPath);
fileDepPath = glob.sync(`**/*${fileDepPath}`, {
cwd: outDir,
absolute: true
})[0];

const oldDepPath = `${publicURL}${path.relative(outDir, fileDepPath)}`;
let newDepPath = path.relative(folderPath, fileDepPath);
Expand All @@ -83,22 +73,60 @@ class FileProcessor {
newDepPath = `${publicURL}${newDepPath}`;
}

// Update the path to the dependency in newly generated file
if (process.env.firstRun === 'false') {
// For the files in the root of the dist folder...
if (folderPath === outDir) {
const fileContent = fs.readFileSync(newFilePath);
// If we have a match, it means that we already have
// a valid link and we shouldn't update it
if (fileContent.includes(newDepPath)) return;
}

replaceInFileSync(
newFilePath,
`${publicURL}${getFileNameFromPath(oldDepPath)}`,
newDepPath
);
return;
}

replaceInFileSync(newFilePath, oldDepPath, newDepPath);
});
}

updateDependants() {
const { oldFilePath, folderName } = this;
const { publicURL } = this.config;

// If the file that is not in the graph was provided (like .DS_Store)
if (!this.depGraph.hasNode(oldFilePath)) return;
const fileDependantsPaths = this.depGraph.dependantsOf(oldFilePath);

fileDependantsPaths.forEach(fileDependantPath => {
fileDependantPath = this.depGraph.getNodeData(fileDependantPath);
fileDependantsPaths.forEach(depPath => {
const fileDependantPath = this.depGraph.getNodeData(depPath);
const oldFileName = getFileNameFromPath(oldFilePath);

const oldDependantPath = `${publicURL}${oldFileName}`;
const newDependantPath = `${publicURL}${folderName}/${oldFileName}`;

// Don't update the path in the entry file because it already contains a valid link
if (
process.env.firstRun === 'false' &&
fileDependantPath === this.depGraph.entryAssetPath
) {
return;
}

// When the map file is processed in the development mode (user specified ".map": "folder")
// It will wrongly update it's path in the dependant file (it collides)
// Example wrong path that we get -> i.e. ../maps/maps/style.2kj52.css.map
if (process.env.firstRun === 'false' && isMapFile(oldFilePath)) {
if (!fs.existsSync(fileDependantPath)) return;
const fileDependantContent = fs.readFileSync(fileDependantPath);
if (fileDependantContent.includes(newDependantPath)) return;
}

replaceInFileSync(fileDependantPath, oldDependantPath, newDependantPath);
});
}
Expand Down
31 changes: 29 additions & 2 deletions lib/OutputGenerator.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
const FileProcessor = require('./FileProcessor');
const { sanitizeFolderName } = require('./Utils');
const {
sanitizeFolderName,
getFilesFromFolder,
isMapFile
} = require('./Utils');

class OutputGenerator {
constructor(depGraph, config) {
Expand All @@ -8,7 +12,7 @@ class OutputGenerator {
}

generateOutputFolder() {
const { pluginFolderConfig } = this.config;
const { pluginFolderConfig, outDir, mode } = this.config;

Object.entries(pluginFolderConfig).forEach(pair => {
const key = pair[0];
Expand All @@ -33,6 +37,29 @@ class OutputGenerator {

this.processFiles(files, sanitizeFolderName(folderName));
});

if (mode === 'production') return;

/**
* Files in the root of the dist folder get overriden each time the user modifies
* them in the source folder and Parcel compiles the changes (development mode)
* Unfortunately, we can't know which file was updated so we simply update the dependency
* paths for all of them. This hits performance a bit on slower machines.
*/
const filesInRootDist = getFilesFromFolder(outDir)
.filter(file => !isMapFile(file))
.map(file => `${outDir}/${file}`);

filesInRootDist.forEach(file => {
const fileProcessor = new FileProcessor(
file,
'',
this.depGraph,
this.config
);

fileProcessor.updateDependecies();
});
}

processFiles(files, folderName) {
Expand Down
9 changes: 9 additions & 0 deletions lib/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ function sanitizeFolderName(folderName) {
return sanitizedFolderName;
}

function getFilesFromFolder(folderPath, fileType = '') {
return fs
.readdirSync(folderPath, { withFileTypes: true })
.filter(item => !item.isDirectory())
.filter(item => item.name.endsWith(fileType))
.map(item => item.name);
}

const isEmptyObject = obj => Object.keys(obj).length === 0;

module.exports = {
Expand All @@ -90,5 +98,6 @@ module.exports = {
isMapFile,
isStyleJSFile,
sanitizeFolderName,
getFilesFromFolder,
isEmptyObject
};
2 changes: 1 addition & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = bundler => {
outputGenerator.generateOutputFolder();

if (mode === 'development') {
process.env.updateFileReferences = false;
process.env.firstRun = false;
}
});
};
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 11 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
{
"name": "parcel-plugin-custom-dist-structure",
"version": "1.0.5",
"version": "1.1.1",
"description": "Parcel plugin that allows you to specify a custom dist structure.",
"main": "lib/index.js",
"scripts": {
"test": "jest"
"test": "jest",
"coverage": "jest --coverage"
},
"jest": {
"collectCoverageFrom": [
"./lib/**/*.js"
]
},
"customDistStructure": {
"config": {
".js": "js",
".css": "css",
".map": "maps",
"images": [
".jpg",
".svg"
Expand All @@ -35,6 +42,7 @@
"license": "MIT",
"dependencies": {
"dependency-graph": "^0.9.0",
"glob": "^7.1.6",
"move-file": "^1.2.0",
"regex-escape": "^3.4.9",
"replace-in-file": "^5.0.2"
Expand All @@ -47,4 +55,4 @@
"parcel-bundler": "^1.12.4",
"parcel-plugin-nuke-dist": "^1.0.1"
}
}
}
68 changes: 68 additions & 0 deletions test/helpers/dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Start parcel in development mode and load the plugin
const path = require('path');
const Bundler = require('parcel-bundler');
const { execSync } = require('child_process');
const { readFileSync, writeFileSync } = require('./utils');
const OutputCustomizer = require('../../lib/index');

const CWD = process.cwd();
let publicUrl = './';
let outDir = 'dist';

if (process.argv.length > 2) {
publicUrl = process.argv[2].split('=')[1];
outDir = process.argv[3].split('=')[1];
}

(async function() {
const entryFile = path.join(CWD, 'test', 'example-src', 'index.html');

const bundler = new Bundler(entryFile, {
publicUrl,
watch: true,
sourceMaps: true,
outDir
});

await OutputCustomizer(bundler);

bundler.on('buildEnd', modifySourceFiles);

function modifySourceFiles() {
// Detach the listener to avoid infinite loop of changes and rebuilds
bundler.off('buildEnd', modifySourceFiles);

const DSstoreFilePath = `${CWD}/${outDir}/.DS_Store`;
const anotherFilePath = `${CWD}/${outDir}/css/file.txt`;

const htmlFilePath = `${CWD}/test/example-src/index.html`;
const cssFilePath = `${CWD}/test/example-src/css/style.css`;

let htmlFileContent = readFileSync(htmlFilePath);
let cssFileContent = readFileSync(cssFilePath);

htmlFileContent += '<a>Check this out</a>';
cssFileContent += 'p { color: red; }';

// DS_Store file goes to the root
writeFileSync(DSstoreFilePath, 'I want to cause some errors :)');
// "anotherFile" goes to the subdirectory
writeFileSync(anotherFilePath, "I'll try to cause them too!");

writeFileSync(htmlFilePath, htmlFileContent);
writeFileSync(cssFilePath, cssFileContent);

// Wait 1 second for parcel to recompile the new changes
setTimeout(async () => {
await bundler.bundle();

// Undo the changes made to the source files
execSync(`git checkout -- ${htmlFilePath}`);
execSync(`git checkout -- ${cssFilePath}`);

process.exit(0);
}, 1000);
}

const bundle = await bundler.bundle();
})();
28 changes: 28 additions & 0 deletions test/helpers/prod.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Generate a production bundle
const path = require('path');
const Bundler = require('parcel-bundler');
const OutputCustomizer = require('../../lib/index');

const CWD = process.cwd();
let publicUrl = './';
let outDir = 'dist';

if (process.argv.length > 2) {
publicUrl = process.argv[2].split('=')[1];
outDir = process.argv[3].split('=')[1];
}

(async function() {
const entryFile = path.join(CWD, 'test', 'example-src', 'index.html');

const bundler = new Bundler(entryFile, {
publicUrl,
watch: false,
sourceMaps: true,
outDir
});

await OutputCustomizer(bundler);

const bundle = await bundler.bundle();
})();
Loading

0 comments on commit a85d81c

Please sign in to comment.