Skip to content

Commit 7eb9005

Browse files
committed
feat(core): Simple fully-qualified path node_modules Block imports.
1 parent e03d99f commit 7eb9005

File tree

4 files changed

+62
-20
lines changed

4 files changed

+62
-20
lines changed

packages/@css-blocks/core/src/importing/NodeJsImporter.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ export interface Alias {
1818
}
1919

2020
export class NodeJsImporter implements Importer {
21-
aliases: Alias[] = [];
22-
constructor(aliases: Alias[] | ObjectDictionary<string> = {}) {
21+
aliases: Alias[];
22+
constructor(aliases: Alias[] | ObjectDictionary<string> = []) {
2323
// Normalize aliases input.
2424
this.aliases = Array.isArray(aliases)
2525
? aliases.slice()
@@ -40,7 +40,8 @@ export class NodeJsImporter implements Importer {
4040
// If absolute, this is the identifier.
4141
if (path.isAbsolute(importPath)) { return importPath; }
4242

43-
// Attempt to resolve to absolute path relative to `from` or `rootDir`. If it exists, return.
43+
// Attempt to resolve to absolute path relative to `from` or `rootDir`.
44+
// If it exists, return.
4445
from = from ? this.filesystemPath(from, config) : from;
4546
let fromDir = from ? path.dirname(from) : config.rootDir;
4647
let resolvedPath = path.resolve(fromDir, importPath);
@@ -52,7 +53,13 @@ export class NodeJsImporter implements Importer {
5253
return path.resolve(alias.path, importPath.substring(alias.alias.length + 1));
5354
}
5455

55-
// If no backup alias, return the previously calculated absolute path where it should be.
56+
// If no alias found, test for a node_module resolution.
57+
try {
58+
return require.resolve(importPath, { paths: [config.rootDir] });
59+
} catch (err) {}
60+
61+
// If no backup alias or node_module fount, return the previously calculated
62+
// absolute path where we expect it should be.
5663
return resolvedPath;
5764
}
5865

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@block-reference unscoped from "package";
2+
@block-reference scoped from "@scoped/package";
3+
4+
:scope {
5+
color: yellow;
6+
}

packages/@css-blocks/core/test/importing-test.ts

+43-14
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,25 @@ import {
1616
} from "../src/importing";
1717

1818
const FIXTURES = path.resolve(__dirname, "..", "..", "test", "fixtures");
19-
const FSI_FIXTURES = path.resolve(FIXTURES, "filesystemImporter");
20-
const ALIAS_FIXTURES = path.resolve(FIXTURES, "pathAliasImporter");
19+
const FSI_FIXTURES = path.join(FIXTURES, "filesystemImporter");
20+
const ALIAS_FIXTURES = path.join(FIXTURES, "pathAliasImporter");
21+
const NODE_MODULE_FIXTURES = path.join(FIXTURES, "nodeModuleImporter");
2122

22-
function getConfiguration(options?: Options): ResolvedConfiguration {
23-
return resolveConfiguration(options, {rootDir: path.join(FSI_FIXTURES)});
23+
function getConfiguration(rootDir: string, options?: Options): ResolvedConfiguration {
24+
return resolveConfiguration(options, { rootDir });
2425
}
2526

2627
function testFSImporter(name: string, importer: Importer) {
2728
describe(name, () => {
2829
it("handles an absolute path without a from identifier", () => {
29-
let config = getConfiguration();
30+
let config = getConfiguration(FSI_FIXTURES);
3031
let filename = path.resolve(FSI_FIXTURES, "a.block.css");
3132
let ident = importer.identifier(null, filename, config);
3233
let resolvedFilename = importer.filesystemPath(ident, config);
3334
assert.equal(resolvedFilename, filename);
3435
});
3536
it("handles an absolute path with a from identifier", () => {
36-
let config = getConfiguration();
37+
let config = getConfiguration(FSI_FIXTURES);
3738
let relativeFilename = path.resolve(FSI_FIXTURES, "a.block.css");
3839
let filename = path.resolve(FSI_FIXTURES, "b.block.css");
3940
let relativeIdent = importer.identifier(null, relativeFilename, config);
@@ -42,29 +43,29 @@ function testFSImporter(name: string, importer: Importer) {
4243
assert.equal(resolvedFilename, filename);
4344
});
4445
it("handles a relative path with a from identifier", () => {
45-
let options = getConfiguration();
46+
let options = getConfiguration(FSI_FIXTURES);
4647
let filename = path.resolve(FSI_FIXTURES, "a.block.css");
4748
let ident = importer.identifier(null, filename, options);
4849
let relativeIdent = importer.identifier(ident, "b.block.css", options);
4950
let resolvedFilename = importer.filesystemPath(relativeIdent, options);
5051
assert.equal(resolvedFilename, path.resolve(FSI_FIXTURES, "b.block.css"));
5152
});
5253
it("resolves a relative path without a from identifier against the root directory", () => {
53-
let options = getConfiguration();
54+
let options = getConfiguration(FSI_FIXTURES);
5455
assert.equal(options.rootDir, FSI_FIXTURES);
5556
let ident = importer.identifier(null, "a.block.css", options);
5657
let resolvedFilename = importer.filesystemPath(ident, options);
5758
assert.equal(resolvedFilename, path.resolve(FSI_FIXTURES, "a.block.css"));
5859
});
5960
it("inspects relative to the root directory", () => {
60-
let options = getConfiguration();
61+
let options = getConfiguration(FSI_FIXTURES);
6162
let filename = path.resolve(FSI_FIXTURES, "a.block.css");
6263
let ident = importer.identifier(null, filename, options);
6364
let inspected = importer.debugIdentifier(ident, options);
6465
assert.equal(inspected, "a.block.css");
6566
});
6667
it("decides syntax based on extension", () => {
67-
let options = getConfiguration();
68+
let options = getConfiguration(FSI_FIXTURES);
6869
let cssIdent = importer.identifier(null, "a.block.css", options);
6970
assert.equal(importer.syntax(cssIdent, options), Syntax.css);
7071
let scssIdent = importer.identifier(null, "scss.block.scss", options);
@@ -79,7 +80,7 @@ function testFSImporter(name: string, importer: Importer) {
7980
assert.equal(importer.syntax(otherIdent, options), Syntax.other);
8081
});
8182
it("imports a file", async () => {
82-
let options = getConfiguration();
83+
let options = getConfiguration(FSI_FIXTURES);
8384
let ident = importer.identifier(null, "a.block.css", options);
8485
let importedFile = await importer.import(ident, options);
8586
assert.deepEqual(importedFile.contents, fs.readFileSync(path.join(FSI_FIXTURES, "a.block.css"), "utf-8"));
@@ -94,6 +95,34 @@ testFSImporter("FilesystemImporter", defaultImporter);
9495
testFSImporter("Default PathAliasImporter", new NodeJsImporter({}));
9596
testFSImporter("Configured PathAliasImporter", new NodeJsImporter({alias: ALIAS_FIXTURES}));
9697

98+
describe("Node Module Importer", () => {
99+
before(function(this: IHookCallbackContext) {
100+
this.importer = new NodeJsImporter();
101+
this.config = getConfiguration(NODE_MODULE_FIXTURES);
102+
});
103+
it("handles un-scoped packages' fully qualified paths", function() {
104+
let filename = "package/blocks/styles.block.css";
105+
let ident = this.importer.identifier(null, filename, this.config);
106+
let resolvedFilename = this.importer.filesystemPath(ident, this.config);
107+
assert.equal(ident, path.join(NODE_MODULE_FIXTURES, "node_modules", filename));
108+
assert.equal(resolvedFilename, path.join(NODE_MODULE_FIXTURES, "node_modules", filename));
109+
});
110+
it("handles scoped packages' fully qualified paths", function() {
111+
let filename = "@scoped/package/blocks/styles.block.css";
112+
let ident = this.importer.identifier(null, filename, this.config);
113+
let resolvedFilename = this.importer.filesystemPath(ident, this.config);
114+
assert.equal(ident, path.join(NODE_MODULE_FIXTURES, "node_modules", filename));
115+
assert.equal(resolvedFilename, path.join(NODE_MODULE_FIXTURES, "node_modules", filename));
116+
});
117+
it("gracefully degrades back to relative lookup for undiscoverable fully qualified paths", function() {
118+
let filename = "@scoped/package/blocks/not-here.block.css";
119+
let ident = this.importer.identifier(null, filename, this.config);
120+
let resolvedFilename = this.importer.filesystemPath(ident, this.config);
121+
assert.equal(ident, path.join(NODE_MODULE_FIXTURES, filename));
122+
assert.equal(resolvedFilename, null);
123+
});
124+
});
125+
97126
describe("PathAliasImporter", () => {
98127
before(function(this: IHookCallbackContext) {
99128
let aliases = {
@@ -103,7 +132,7 @@ describe("PathAliasImporter", () => {
103132
this.importer = new NodeJsImporter(aliases);
104133
});
105134
it("identifies relative to an alias", function() {
106-
let options = getConfiguration();
135+
let options = getConfiguration(FSI_FIXTURES);
107136
let importer: Importer = this.importer;
108137
let ident = importer.identifier(null, "pai/alias1.block.css", options);
109138
let actualFilename = importer.filesystemPath(ident, options);
@@ -113,7 +142,7 @@ describe("PathAliasImporter", () => {
113142
assert.equal("pai/alias1.block.css", inspected);
114143
});
115144
it("produces the same identifier via different aliases", function() {
116-
let options = getConfiguration();
145+
let options = getConfiguration(FSI_FIXTURES);
117146
let importer: Importer = this.importer;
118147
let actualFilename = path.resolve(ALIAS_FIXTURES, "alias_subdirectory", "sub.block.css");
119148
let ident1 = importer.identifier(null, "pai/alias_subdirectory/sub.block.css", options);
@@ -129,7 +158,7 @@ describe("PathAliasImporter", () => {
129158
assert.equal(inspected2, "sub/sub.block.css");
130159
});
131160
it("imports an aliased file", async function() {
132-
let options = getConfiguration();
161+
let options = getConfiguration(FSI_FIXTURES);
133162
let importer: Importer = this.importer;
134163
let ident = importer.identifier(null, "sub/sub.block.css", options);
135164
let importedFile = await importer.import(ident, options);

packages/@css-blocks/webpack/test/util/MockImportRegistry.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { assert } from "chai";
22
import * as path from "path";
33

4-
import { ImportedFile, Importer, PathBasedImporter, ResolvedConfiguration as CSSBlocksConfiguration, Syntax } from "@css-blocks/core";
4+
import { ImportedFile, Importer, NodeJsImporter, ResolvedConfiguration as CSSBlocksConfiguration, Syntax } from "@css-blocks/core";
55
import { ObjectDictionary } from "@opticss/util";
66

77
const PROJECT_DIR = path.resolve(__dirname, "../../..");
88

99
export type SourceRegistry = ObjectDictionary<string>;
1010
export type ImportedFiles = ObjectDictionary<boolean>;
1111

12-
export class MockImporter extends PathBasedImporter {
12+
export class MockImporter extends NodeJsImporter {
1313
registry: MockImportRegistry;
1414
constructor(registry: MockImportRegistry) {
1515
super();

0 commit comments

Comments
 (0)