Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
874 changes: 801 additions & 73 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"ignore": "^5.1.4",
"istextorbinary": "^3.3.0",
"jest": "^29.7.0",
"memfs": "^3.0.1",
"memfs": "^4.56.2",
"walk-sync": "^2.0.2"
}
}
1 change: 1 addition & 0 deletions packages/cli/src/cmd/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ function build(userSpecifiedRoot, output, options) {
+ '\n'
+ 'To create a new MarkBind site, run:\n'
+ ' markbind init');
cliUtil.cleanupFailedMarkbindBuild(userSpecifiedRoot);
process.exitCode = 1;
process.exit();
}
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/cmd/serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ function serve(userSpecifiedRoot, options) {
+ '\n'
+ 'To create a new MarkBind site, run:\n'
+ ' markbind init');
cliUtil.cleanupFailedMarkbindBuild(userSpecifiedRoot);
process.exitCode = 1;
process.exit();
}
Expand Down
7 changes: 7 additions & 0 deletions packages/cli/src/util/cliUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,11 @@ module.exports = {
}
return path.dirname(foundConfigPath);
},
cleanupFailedMarkbindBuild: () => {
const markbindDir = path.join(process.cwd(), '_markbind');
if (fs.pathExistsSync(markbindDir)) {
// delete _markbind/ folder and contents
fs.rmSync(markbindDir, { recursive: true, force: true });
}
},
};
54 changes: 53 additions & 1 deletion packages/cli/test/functional/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ const execOptions = {
stdio: ['inherit', 'inherit', 'inherit'],
};

const expectedErrors = ["URLs are not allowed in the 'src' attribute"];
const expectedErrors = [
"URLs are not allowed in the 'src' attribute",
'No config file found in parent directories of',
'This directory does not appear to contain a valid MarkBind site.'
+ ' Check that you are running the command in the correct directory!',
];

logger.info(
`The following ${
Expand Down Expand Up @@ -92,4 +97,51 @@ testTemplateSites.forEach((templateAndSitePath) => {
fs.removeSync(path.resolve(__dirname, siteCreationTempPath));
});

function testEmptyDirectoryBuild() {
const siteRootName = 'test_site_empty';
const siteRootPath = path.join(__dirname, siteRootName);

const emptySiteName = 'empty_dir';
const emptySitePath = path.join(siteRootPath, emptySiteName);

const expectedSiteName = 'expected';
const expectedSitePath = path.join(siteRootPath, expectedSiteName);

const execOptionsWithCwd = {
stdio: ['inherit', 'inherit', 'inherit'],
cwd: emptySitePath, // Set the working directory to testEmptyPath
};

console.log(`Running ${siteRootName} test`);

try {
// Ensure test_site_empty/empty_dir and test_site_empty/expected directories exist
fs.ensureDirSync(emptySitePath);
fs.ensureDirSync(expectedSitePath);

// Try to build in empty directory (should fail with specific error)
try {
execSync(`node ../../../../index.js build ${emptySitePath}`, execOptionsWithCwd);
printFailedMessage(new Error('Expected build to fail but it succeeded'), siteRootName);
process.exit(1);
} catch (err) {
// Verify that test_empty directory remains empty using compare()
try {
compare(siteRootName, 'expected', 'empty_dir', [], true);
} catch (compareErr) {
printFailedMessage(compareErr, siteRootName);
// Reset test_site_empty/empty_dir
fs.emptyDirSync(emptySitePath);
process.exit(1);
}
}
} finally {
// Reset test_site_empty/empty_dir
fs.emptyDirSync(emptySitePath);
}
}

// Run the empty directory test
testEmptyDirectoryBuild();

console.log('Test result: PASSED');
34 changes: 31 additions & 3 deletions packages/cli/test/functional/testUtil/compare.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,48 @@ function filterPageVueRenderFiles(filePaths) {
}

/**
* Compares files generated by the build process against the expected files.
* Gets directory structure as a sorted array of relative directory paths
* @param {string} dirPath - Existing directory path to analyze
* @returns {string[]} Sorted array of relative directory paths
*/
function getDirectoryStructure(dirPath) {
const allPaths = walkSync(dirPath, { directories: true, globs: ['**/*'] });
return allPaths
.filter(p => fs.statSync(path.join(dirPath, p))
.isDirectory())
.map(p => p.replace(/\\/g, '/')) // Normalize path separators
.sort();
}

/**
* Compares files and optionally directories generated by the build process against the expected files.
* Throws an error on:
* - Missing or extra files
* - Files differing in content
* - Ignored files not present
* @param {string} root - Root path containing both expected and actual site folders
* @param {string} expectedSiteRelativePath - Relative path to expected site output (default: "expected")
* @param {string} siteRelativePath - Relative path to actual generated site output (default: "_site")
* @param {string} ignoredPaths - Specify any paths to ignore for comparison, but still check for existence.
* @param {string[]} ignoredPaths - Specify any paths to ignore for comparison, but still check for existence.
* @param {boolean} compareDirectories - Whether to compare directory structures (default: false)
*/
function compare(root, expectedSiteRelativePath = 'expected', siteRelativePath = '_site', ignoredPaths = []) {
function compare(root, expectedSiteRelativePath = 'expected', siteRelativePath = '_site',
ignoredPaths = [], compareDirectories = false) {
const expectedDirectory = path.join(root, expectedSiteRelativePath);
const actualDirectory = path.join(root, siteRelativePath);

// Get directory structures if needed
if (compareDirectories) {
const expectedDirs = getDirectoryStructure(expectedDirectory);
const actualDirs = getDirectoryStructure(actualDirectory);

if (!_.isEqual(expectedDirs, actualDirs)) {
throw new Error('Directory structures differ!'
+ `\nExpected: ${JSON.stringify(expectedDirs, null, 2)}`
+ `\nActual: ${JSON.stringify(actualDirs, null, 2)}`);
}
}

let expectedPaths = walkSync(expectedDirectory, { directories: false });
let actualPaths = walkSync(actualDirectory, { directories: false });

Expand Down
45 changes: 45 additions & 0 deletions packages/cli/test/unit/cliUtil.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,48 @@ test('findRootFolder without user specified root throws error if no parent dirs
})
.toThrow(`No config file found in parent directories of ${nestedDir}`);
});

// Tests for cleanupFailedMarkbindBuild function
test('cleanupFailedMarkbindBuild removes _markbind directory in current working directory', () => {
const currentWorkingDir = '/test/root';
const json = {
'/test/root/_markbind/': {
'file1.txt': 'content',
'subdir/': {
'file2.txt': 'content',
},
},
};
fs.vol.fromJSON(json, '');
process.cwd = jest.fn().mockReturnValue(currentWorkingDir);
cliUtil.cleanupFailedMarkbindBuild();
expect(fs.vol.existsSync(path.join(currentWorkingDir, '_markbind'))).toBe(false);
});

test('cleanupFailedMarkbindBuild handles missing _markbind directory gracefully', () => {
const currentWorkingDir = '/test/root';
const json = {
'/test/root/': {
'otherfile.txt': 'content',
},
};
fs.vol.fromJSON(json, '');
process.cwd = jest.fn().mockReturnValue(currentWorkingDir);
// Should not throw an error
expect(() => {
cliUtil.cleanupFailedMarkbindBuild();
}).not.toThrow();
});

test('cleanupFailedMarkbindBuild works with different working directories', () => {
const currentWorkingDir = '/different/working/dir';
const json = {
'/different/working/dir/_markbind/': {
'test.txt': 'content',
},
};
fs.vol.fromJSON(json, '');
process.cwd = jest.fn().mockReturnValue(currentWorkingDir);
cliUtil.cleanupFailedMarkbindBuild();
expect(fs.vol.existsSync(path.join(currentWorkingDir, '_markbind'))).toBe(false);
});