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
26 changes: 14 additions & 12 deletions src/services/cssNavigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,19 +423,15 @@ export class CSSNavigation {
}

// Treat bare module names (“bootstrap/...”) like sass-loader does
const startsWithSchemeRegex = /^\w[\w\d+.-]:/;

const isBareImport = !target.startsWith('.') // not ./ or ../
&& !target.startsWith('/') // not workspace-absolute
&& !startsWithSchemeRegex.test(target); // not a scheme (file://, http://, etc.)

if (isBareImport) {
const moduleRef = await this.mapReference(
await this.resolveModuleReference(target, documentUri, documentContext),
isRawLink);
if (moduleRef) { return moduleRef; }
if (this.resolveModuleReferences && importIsBare(target)) {
const resolvedModuleRef = await this.resolveModuleReference(target, documentUri, documentContext);
const moduleRef = await this.mapReference(resolvedModuleRef, isRawLink);

if (moduleRef != null) {
return moduleRef;
}
}

const ref = await this.mapReference(
documentContext.resolveReference(target, documentUri), isRawLink);

Expand Down Expand Up @@ -588,6 +584,12 @@ function toTwoDigitHex(n: number): string {
return r.length !== 2 ? '0' + r : r;
}

function importIsBare(target: string): boolean {
return !target.startsWith('.') // not ./ or ../
&& !target.startsWith('/') // not workspace-absolute
&& !startsWithSchemeRegex.test(target); // not a scheme (file://, http://, etc.)
}

export function getModuleNameFromPath(path: string) {
const firstSlash = path.indexOf('/');
if (firstSlash === -1) {
Expand Down
46 changes: 0 additions & 46 deletions src/test/scss/schemeImportLinks.test.ts

This file was deleted.

18 changes: 0 additions & 18 deletions src/test/scss/scssNavigation-node-modules.test.ts

This file was deleted.

65 changes: 59 additions & 6 deletions src/test/scss/scssNavigation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import * as nodes from '../../parser/cssNodes';
import { assertSymbolsInScope, assertScopesAndSymbols, assertHighlights, assertColorSymbols, assertLinks, newRange, getTestResource, assertDocumentSymbols } from '../css/navigation.test';
import { getSCSSLanguageService, DocumentLink, TextDocument, SymbolKind, LanguageSettings } from '../../cssLanguageService';
import { getSCSSLanguageService, DocumentLink, TextDocument, SymbolKind, LanguageSettings, DocumentContext } from '../../cssLanguageService';
import * as assert from 'assert';
import * as path from 'path';
import { URI } from 'vscode-uri';
Expand All @@ -20,11 +20,11 @@ function getSCSSLS() {
function aliasSettings(): LanguageSettings {
return {
"importAliases": {
"@SassStylesheet": "/src/assets/styles.scss",
"@NoUnderscoreDir/": "/noUnderscore/",
"@UnderscoreDir/": "/underscore/",
"@BothDir/": "/both/",
}
"@SassStylesheet": "/src/assets/styles.scss",
"@NoUnderscoreDir/": "/noUnderscore/",
"@UnderscoreDir/": "/underscore/",
"@BothDir/": "/both/",
}
};
}

Expand Down Expand Up @@ -57,6 +57,21 @@ async function assertNoDynamicLinks(docUri: string, input: string, extecedTarget

}

function createDocument(contents: string, uri = 'file:///test.scss') {
return TextDocument.create(uri, 'scss', 0, contents);
}

const dummyContext: DocumentContext = {
resolveReference: (ref: string, _base: string) => ref
};

async function getLinks(contents: string) {
const ls = getSCSSLS();
const doc = createDocument(contents);
const stylesheet = ls.parseStylesheet(doc);
return ls.findDocumentLinks2(doc, stylesheet, dummyContext);
}

suite('SCSS - Navigation', () => {

suite('Scopes and Symbols', () => {
Expand Down Expand Up @@ -353,4 +368,42 @@ suite('SCSS - Navigation', () => {
});
});

suite('URL Scheme Imports', () => {

test('http scheme import is treated as absolute URL, not bare import', async () => {
const links = await getLinks(`@import "http://example.com/foo.css";`);
assert.strictEqual(links.length, 1);
assert.strictEqual(links[0].target, 'http://example.com/foo.css');
});

test('https scheme import is treated as absolute URL, not bare import', async () => {
const links = await getLinks(`@import "https://cdn.example.com/reset.css";`);
assert.strictEqual(links.length, 1);
assert.strictEqual(links[0].target, 'https://cdn.example.com/reset.css');
});

test('file scheme import is treated as absolute URL, not bare import', async () => {
const links = await getLinks(`@import "file:///Users/test/project/styles/base.scss";`);
assert.strictEqual(links.length, 1);
assert.strictEqual(links[0].target, 'file:///Users/test/project/styles/base.scss');
});

test('custom scheme import (vscode-resource) is treated as absolute URL, not bare import', async () => {
const links = await getLinks(`@import "vscode-resource://file/some.css";`);
assert.strictEqual(links.length, 1);
assert.strictEqual(links[0].target, 'vscode-resource://file/some.css');
});
});

suite('Bare Imports', () => {

test('resolves bare import path on Windows', async () => {
const ls = getSCSSLS();

const doc = TextDocument.create('file:///c:/proj/app.scss', 'scss', 1, "@import 'bootstrap/scss/variables';");
const links = await ls.findDocumentLinks2(doc, ls.parseStylesheet(doc), getDocumentContext('c:/proj'));
const expected = URI.file('c:/proj/node_modules/bootstrap/scss/_variables.scss').toString();
assert.strictEqual(links[0].target, expected);
});
});
});