diff --git a/src/index.ts b/src/index.ts index d720c4b..512a45a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -175,17 +175,40 @@ export class LinkChecker extends EventEmitter { ); } + if (options.serverRoot) { + options.serverRoot = path.normalize(options.serverRoot); + } + // expand globs into paths if (!isUrlType) { const paths: string[] = []; - for (const path of options.path) { - const expandedPaths = await glob(path); + for (const filePath of options.path) { + // The glob path provided is relative to the serverRoot. For example, + // if the serverRoot is test/fixtures/nested, and the glob is "*/*.html", + // The glob needs to be calculated from the serverRoot directory. + const fullPath = options.serverRoot + ? path.join(options.serverRoot, filePath) + : filePath; + const expandedPaths = await glob(fullPath); if (expandedPaths.length === 0) { throw new Error( - `The provided glob "${path}" returned 0 results. The current working directory is "${process.cwd()}".` + `The provided glob "${filePath}" returned 0 results. The current working directory is "${process.cwd()}".` ); } - paths.push(...expandedPaths); + // After resolving the globs, the paths need to be returned to their + // original form, without the serverRoot included in the path. + for (let p of expandedPaths) { + p = path.normalize(p); + if (options.serverRoot) { + const contractedPath = p + .split(path.sep) + .slice(options.serverRoot.split(path.sep).length) + .join(path.sep); + paths.push(contractedPath); + } else { + paths.push(p); + } + } } options.path = paths; } diff --git a/test/fixtures/nested/doll1/index.html b/test/fixtures/nested/doll1/index.html new file mode 100644 index 0000000..8ee87a2 --- /dev/null +++ b/test/fixtures/nested/doll1/index.html @@ -0,0 +1,5 @@ + + +just follow a link + + diff --git a/test/fixtures/nested/doll2/index.html b/test/fixtures/nested/doll2/index.html new file mode 100644 index 0000000..0afa382 --- /dev/null +++ b/test/fixtures/nested/doll2/index.html @@ -0,0 +1,5 @@ + + +just follow a link + + diff --git a/test/test.ts b/test/test.ts index 868d7b3..ebde3c7 100644 --- a/test/test.ts +++ b/test/test.ts @@ -304,7 +304,6 @@ describe('linkinator', () => { it('should allow overriding the server root', async () => { const results = await check({ serverRoot: 'test/fixtures/markdown', - markdown: true, path: 'README.md', }); assert.strictEqual(results.links.length, 3); @@ -385,7 +384,6 @@ describe('linkinator', () => { const consoleSpy = sinon.stub(console, 'log'); const results = await check({ path: 'test/fixtures/markdown/README.md', - markdown: true, }); assert.ok(results.passed); assert.ok(consoleSpy.calledOnce); @@ -394,7 +392,6 @@ describe('linkinator', () => { it('should respect globs', async () => { const results = await check({ path: 'test/fixtures/markdown/**/*.md', - markdown: true, }); assert.ok(results.passed); assert.strictEqual(results.links.length, 6); @@ -446,6 +443,36 @@ describe('linkinator', () => { assert.ok(/Nock: Disallowed net connect for/.test(err.message)); }); + it('should respect server root with globs', async () => { + const scope = nock('http://fake.local') + .get('/doll1') + .reply(200) + .get('/doll2') + .reply(200); + const results = await check({ + serverRoot: 'test/fixtures/nested', + path: '*/*.html', + }); + assert.strictEqual(results.links.length, 4); + assert.ok(results.passed); + scope.done(); + }); + + it('should respect absolute server root', async () => { + const scope = nock('http://fake.local') + .get('/doll1') + .reply(200) + .get('/doll2') + .reply(200); + const results = await check({ + serverRoot: path.resolve('test/fixtures/nested'), + path: '*/*.html', + }); + assert.strictEqual(results.links.length, 4); + assert.ok(results.passed); + scope.done(); + }); + it('should scan links in tags', async () => { const scope = nock('http://fake.local').head('/').reply(200); const results = await check({path: 'test/fixtures/twittercard'});