Skip to content

Commit e8e2359

Browse files
committed
feature #525 Allow setOutputPath to create nested directories (Lyrkan)
This PR was merged into the master branch. Discussion ---------- Allow setOutputPath to create nested directories This PR changes the check that is currently made when calling `setOutputPath` in order to allow the creation of nested directories (closes #189). Note that it doesn't remove it entirely since it still prevents the creation of more than one level when the output path is located outside of the context path. For instance, given the context is `/home/foo/bar/`: ```js // Will work Encore.setOutputPath('build'); // Will work, even if "/home/foo/bar/public" does not exist Encore.setOutputPath('public/build/output'); // Will work, even if "/home/foo/public" does not exist Encore.setOutputPath('../public'); // Will work if only "/home/foo/public/build" does not exist // ... but **won't** work if "/home/foo/public" does not exist Encore.setOutputPath('../public/build'); ``` Commits ------- ff200af Allow setOutputPath to create nested directories
2 parents 078ddbc + ff200af commit e8e2359

File tree

2 files changed

+68
-17
lines changed

2 files changed

+68
-17
lines changed

lib/WebpackConfig.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,23 @@ class WebpackConfig {
130130
}
131131

132132
if (!fs.existsSync(outputPath)) {
133-
// for safety, we won't recursively create directories
134-
// this might be a sign that the user has specified
135-
// an incorrect path
136-
if (!fs.existsSync(path.dirname(outputPath))) {
137-
throw new Error(`outputPath directory does not exist: ${outputPath}. Please check the path you're passing to setOutputPath() or create this directory`);
133+
// If the parent of the output directory does not exist either
134+
// check if it is located under the context directory before
135+
// creating it and its parent.
136+
const parentPath = path.dirname(outputPath);
137+
if (!fs.existsSync(parentPath)) {
138+
const context = path.resolve(this.getContext());
139+
if (outputPath.indexOf(context) !== 0) {
140+
throw new Error(`outputPath directory "${outputPath}" does not exist and is not located under the context directory "${context}". Please check the path you're passing to setOutputPath() or create this directory.`);
141+
}
142+
143+
parentPath.split(path.sep).reduce((previousPath, directory) => {
144+
const newPath = path.resolve(previousPath, directory);
145+
if (!fs.existsSync(newPath)) {
146+
fs.mkdirSync(newPath);
147+
}
148+
return newPath;
149+
}, path.sep);
138150
}
139151

140152
fs.mkdirSync(outputPath);

test/WebpackConfig.js

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,32 @@ function createConfig() {
2828
describe('WebpackConfig object', () => {
2929

3030
describe('setOutputPath', () => {
31+
const removeDirectory = (targetPath) => {
32+
if (fs.existsSync(targetPath)) {
33+
const files = fs.readdirSync(targetPath);
34+
for (const file of files) {
35+
const filePath = path.resolve(targetPath, file);
36+
if (fs.lstatSync(filePath).isDirectory()) {
37+
removeDirectory(filePath);
38+
} else {
39+
fs.unlinkSync(filePath);
40+
}
41+
}
42+
43+
fs.rmdirSync(targetPath);
44+
}
45+
};
46+
47+
// Make sure the newly created directories are removed
48+
// before and after each test
49+
const cleanupNewDirectories = () => {
50+
removeDirectory(path.resolve(__dirname, 'new_dir'));
51+
removeDirectory(path.resolve(__dirname, '..', 'new_dir'));
52+
};
53+
54+
beforeEach(cleanupNewDirectories);
55+
afterEach(cleanupNewDirectories);
56+
3157
it('use absolute, existent path', () => {
3258
const config = createConfig();
3359
config.setOutputPath(__dirname);
@@ -37,15 +63,12 @@ describe('WebpackConfig object', () => {
3763

3864
it('relative path, becomes absolute', () => {
3965
const config = createConfig();
40-
config.setOutputPath('assets');
66+
config.setOutputPath('new_dir');
4167

4268
// __dirname is the context
4369
expect(config.outputPath).to.equal(
44-
path.join(__dirname, '/assets')
70+
path.join(__dirname, '/new_dir')
4571
);
46-
47-
// cleanup!
48-
fs.rmdirSync(path.join(__dirname, '/assets'));
4972
});
5073

5174
it('non-existent path creates directory', () => {
@@ -56,23 +79,39 @@ describe('WebpackConfig object', () => {
5679

5780
const config = createConfig();
5881
config.setOutputPath(targetPath);
82+
expect(fs.existsSync(config.outputPath)).to.be.true;
83+
});
5984

85+
it('non-existent directory, 3 levels deep is created correctly', () => {
86+
var targetPath = path.join(__dirname, 'new_dir', 'subdir1', 'subdir2');
87+
if (fs.existsSync(targetPath)) {
88+
fs.rmdirSync(targetPath);
89+
}
90+
91+
const config = createConfig();
92+
config.setOutputPath(targetPath);
6093
expect(fs.existsSync(config.outputPath)).to.be.true;
94+
});
95+
96+
it('non-existent path outside of the context directory works if only one directory has to be created', () => {
97+
var targetPath = path.join(__dirname, '..', 'new_dir');
98+
if (fs.existsSync(targetPath)) {
99+
fs.rmdirSync(targetPath);
100+
}
61101

62-
// cleanup!
63-
fs.rmdirSync(targetPath);
102+
const config = createConfig();
103+
config.setOutputPath(targetPath);
104+
expect(fs.existsSync(config.outputPath)).to.be.true;
64105
});
65106

66-
it('non-existent directory, 2 levels deep throws error', () => {
67-
var targetPath = path.join(__dirname, 'new_dir', 'subdir');
107+
it('non-existent path outside of the context directory throws an error if more than one directory has to be created', () => {
108+
var targetPath = path.join(__dirname, '..', 'new_dir', 'subdir');
68109
if (fs.existsSync(targetPath)) {
69110
fs.rmdirSync(targetPath);
70111
}
71112

72113
const config = createConfig();
73-
expect(() => {
74-
config.setOutputPath(targetPath);
75-
}).to.throw('create this directory');
114+
expect(() => config.setOutputPath(targetPath)).to.throw('create this directory');
76115
});
77116
});
78117

0 commit comments

Comments
 (0)