Skip to content

Commit

Permalink
feat(core): Simple fully-qualified path node_modules Block imports.
Browse files Browse the repository at this point in the history
  • Loading branch information
amiller-gh committed Nov 26, 2018
1 parent e03d99f commit 7eb9005
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 20 deletions.
15 changes: 11 additions & 4 deletions packages/@css-blocks/core/src/importing/NodeJsImporter.ts
Expand Up @@ -18,8 +18,8 @@ export interface Alias {
}

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

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

// If no backup alias, return the previously calculated absolute path where it should be.
// If no alias found, test for a node_module resolution.
try {
return require.resolve(importPath, { paths: [config.rootDir] });
} catch (err) {}

// If no backup alias or node_module fount, return the previously calculated
// absolute path where we expect it should be.
return resolvedPath;
}

Expand Down
@@ -0,0 +1,6 @@
@block-reference unscoped from "package";
@block-reference scoped from "@scoped/package";

:scope {
color: yellow;
}
57 changes: 43 additions & 14 deletions packages/@css-blocks/core/test/importing-test.ts
Expand Up @@ -16,24 +16,25 @@ import {
} from "../src/importing";

const FIXTURES = path.resolve(__dirname, "..", "..", "test", "fixtures");
const FSI_FIXTURES = path.resolve(FIXTURES, "filesystemImporter");
const ALIAS_FIXTURES = path.resolve(FIXTURES, "pathAliasImporter");
const FSI_FIXTURES = path.join(FIXTURES, "filesystemImporter");
const ALIAS_FIXTURES = path.join(FIXTURES, "pathAliasImporter");
const NODE_MODULE_FIXTURES = path.join(FIXTURES, "nodeModuleImporter");

function getConfiguration(options?: Options): ResolvedConfiguration {
return resolveConfiguration(options, {rootDir: path.join(FSI_FIXTURES)});
function getConfiguration(rootDir: string, options?: Options): ResolvedConfiguration {
return resolveConfiguration(options, { rootDir });
}

function testFSImporter(name: string, importer: Importer) {
describe(name, () => {
it("handles an absolute path without a from identifier", () => {
let config = getConfiguration();
let config = getConfiguration(FSI_FIXTURES);
let filename = path.resolve(FSI_FIXTURES, "a.block.css");
let ident = importer.identifier(null, filename, config);
let resolvedFilename = importer.filesystemPath(ident, config);
assert.equal(resolvedFilename, filename);
});
it("handles an absolute path with a from identifier", () => {
let config = getConfiguration();
let config = getConfiguration(FSI_FIXTURES);
let relativeFilename = path.resolve(FSI_FIXTURES, "a.block.css");
let filename = path.resolve(FSI_FIXTURES, "b.block.css");
let relativeIdent = importer.identifier(null, relativeFilename, config);
Expand All @@ -42,29 +43,29 @@ function testFSImporter(name: string, importer: Importer) {
assert.equal(resolvedFilename, filename);
});
it("handles a relative path with a from identifier", () => {
let options = getConfiguration();
let options = getConfiguration(FSI_FIXTURES);
let filename = path.resolve(FSI_FIXTURES, "a.block.css");
let ident = importer.identifier(null, filename, options);
let relativeIdent = importer.identifier(ident, "b.block.css", options);
let resolvedFilename = importer.filesystemPath(relativeIdent, options);
assert.equal(resolvedFilename, path.resolve(FSI_FIXTURES, "b.block.css"));
});
it("resolves a relative path without a from identifier against the root directory", () => {
let options = getConfiguration();
let options = getConfiguration(FSI_FIXTURES);
assert.equal(options.rootDir, FSI_FIXTURES);
let ident = importer.identifier(null, "a.block.css", options);
let resolvedFilename = importer.filesystemPath(ident, options);
assert.equal(resolvedFilename, path.resolve(FSI_FIXTURES, "a.block.css"));
});
it("inspects relative to the root directory", () => {
let options = getConfiguration();
let options = getConfiguration(FSI_FIXTURES);
let filename = path.resolve(FSI_FIXTURES, "a.block.css");
let ident = importer.identifier(null, filename, options);
let inspected = importer.debugIdentifier(ident, options);
assert.equal(inspected, "a.block.css");
});
it("decides syntax based on extension", () => {
let options = getConfiguration();
let options = getConfiguration(FSI_FIXTURES);
let cssIdent = importer.identifier(null, "a.block.css", options);
assert.equal(importer.syntax(cssIdent, options), Syntax.css);
let scssIdent = importer.identifier(null, "scss.block.scss", options);
Expand All @@ -79,7 +80,7 @@ function testFSImporter(name: string, importer: Importer) {
assert.equal(importer.syntax(otherIdent, options), Syntax.other);
});
it("imports a file", async () => {
let options = getConfiguration();
let options = getConfiguration(FSI_FIXTURES);
let ident = importer.identifier(null, "a.block.css", options);
let importedFile = await importer.import(ident, options);
assert.deepEqual(importedFile.contents, fs.readFileSync(path.join(FSI_FIXTURES, "a.block.css"), "utf-8"));
Expand All @@ -94,6 +95,34 @@ testFSImporter("FilesystemImporter", defaultImporter);
testFSImporter("Default PathAliasImporter", new NodeJsImporter({}));
testFSImporter("Configured PathAliasImporter", new NodeJsImporter({alias: ALIAS_FIXTURES}));

describe("Node Module Importer", () => {
before(function(this: IHookCallbackContext) {
this.importer = new NodeJsImporter();
this.config = getConfiguration(NODE_MODULE_FIXTURES);
});
it("handles un-scoped packages' fully qualified paths", function() {
let filename = "package/blocks/styles.block.css";
let ident = this.importer.identifier(null, filename, this.config);
let resolvedFilename = this.importer.filesystemPath(ident, this.config);
assert.equal(ident, path.join(NODE_MODULE_FIXTURES, "node_modules", filename));
assert.equal(resolvedFilename, path.join(NODE_MODULE_FIXTURES, "node_modules", filename));
});
it("handles scoped packages' fully qualified paths", function() {
let filename = "@scoped/package/blocks/styles.block.css";
let ident = this.importer.identifier(null, filename, this.config);
let resolvedFilename = this.importer.filesystemPath(ident, this.config);
assert.equal(ident, path.join(NODE_MODULE_FIXTURES, "node_modules", filename));
assert.equal(resolvedFilename, path.join(NODE_MODULE_FIXTURES, "node_modules", filename));
});
it("gracefully degrades back to relative lookup for undiscoverable fully qualified paths", function() {
let filename = "@scoped/package/blocks/not-here.block.css";
let ident = this.importer.identifier(null, filename, this.config);
let resolvedFilename = this.importer.filesystemPath(ident, this.config);
assert.equal(ident, path.join(NODE_MODULE_FIXTURES, filename));
assert.equal(resolvedFilename, null);
});
});

describe("PathAliasImporter", () => {
before(function(this: IHookCallbackContext) {
let aliases = {
Expand All @@ -103,7 +132,7 @@ describe("PathAliasImporter", () => {
this.importer = new NodeJsImporter(aliases);
});
it("identifies relative to an alias", function() {
let options = getConfiguration();
let options = getConfiguration(FSI_FIXTURES);
let importer: Importer = this.importer;
let ident = importer.identifier(null, "pai/alias1.block.css", options);
let actualFilename = importer.filesystemPath(ident, options);
Expand All @@ -113,7 +142,7 @@ describe("PathAliasImporter", () => {
assert.equal("pai/alias1.block.css", inspected);
});
it("produces the same identifier via different aliases", function() {
let options = getConfiguration();
let options = getConfiguration(FSI_FIXTURES);
let importer: Importer = this.importer;
let actualFilename = path.resolve(ALIAS_FIXTURES, "alias_subdirectory", "sub.block.css");
let ident1 = importer.identifier(null, "pai/alias_subdirectory/sub.block.css", options);
Expand All @@ -129,7 +158,7 @@ describe("PathAliasImporter", () => {
assert.equal(inspected2, "sub/sub.block.css");
});
it("imports an aliased file", async function() {
let options = getConfiguration();
let options = getConfiguration(FSI_FIXTURES);
let importer: Importer = this.importer;
let ident = importer.identifier(null, "sub/sub.block.css", options);
let importedFile = await importer.import(ident, options);
Expand Down
4 changes: 2 additions & 2 deletions packages/@css-blocks/webpack/test/util/MockImportRegistry.ts
@@ -1,15 +1,15 @@
import { assert } from "chai";
import * as path from "path";

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

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

export type SourceRegistry = ObjectDictionary<string>;
export type ImportedFiles = ObjectDictionary<boolean>;

export class MockImporter extends PathBasedImporter {
export class MockImporter extends NodeJsImporter {
registry: MockImportRegistry;
constructor(registry: MockImportRegistry) {
super();
Expand Down

0 comments on commit 7eb9005

Please sign in to comment.