Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[draft] feat: resolve style field in package json #388

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 11 additions & 3 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"anotherhashishere",
"arcanis",
"Builtins",
"burhanuday",
"Burhanuddin",
"codecov",
"complexm",
"endregion",
"entrypoints",
Expand All @@ -24,15 +27,20 @@
"sokra",
"subpaths",
"tapable",
"Udaipurwala",
"undescribed",
"unreviewed",
"vankop",
"wpck",
"zapp",
"zipp",
"zippi",
"zizizi",
"codecov"
"zizizi"
],
"ignorePaths": ["package.json", "yarn.lock", "coverage", "*.log"]
"ignorePaths": [
"package.json",
"yarn.lock",
"coverage",
"*.log"
]
}
17 changes: 16 additions & 1 deletion lib/ResolverFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const SymlinkPlugin = require("./SymlinkPlugin");
const TryNextPlugin = require("./TryNextPlugin");
const UnsafeCachePlugin = require("./UnsafeCachePlugin");
const UseFilePlugin = require("./UseFilePlugin");
const StyleFieldPlugin = require("./StyleFieldPlugin");

/** @typedef {import("./AliasPlugin").AliasOption} AliasOptionEntry */
/** @typedef {import("./ExtensionAliasPlugin").ExtensionAliasOption} ExtensionAliasOption */
Expand Down Expand Up @@ -64,6 +65,7 @@ const UseFilePlugin = require("./UseFilePlugin");
* @property {string[]=} conditionNames A list of exports field condition names.
* @property {boolean=} enforceExtension Enforce that a extension from extensions must be used
* @property {(string | string[])[]=} exportsFields A list of exports fields in description files
* @property {(string | string[])=} styleFields A list of style fields in description files
* @property {(string | string[])[]=} importsFields A list of imports fields in description files
* @property {string[]=} extensions A list of extensions which should be tried for files
* @property {FileSystem} fileSystem The file system which should be used
Expand Down Expand Up @@ -97,6 +99,7 @@ const UseFilePlugin = require("./UseFilePlugin");
* @property {boolean} enforceExtension
* @property {Set<string | string[]>} exportsFields
* @property {Set<string | string[]>} importsFields
* @property {Set<string>} styleFields
* @property {Set<string>} extensions
* @property {FileSystem} fileSystem
* @property {object | false} unsafeCache
Expand Down Expand Up @@ -194,6 +197,7 @@ function createOptions(options) {
: true,
exportsFields: new Set(options.exportsFields || ["exports"]),
importsFields: new Set(options.importsFields || ["imports"]),
styleFields: new Set(options.styleFields || ["style"]),
conditionNames: new Set(options.conditionNames),
descriptionFiles: Array.from(
new Set(options.descriptionFiles || ["package.json"])
Expand Down Expand Up @@ -284,7 +288,8 @@ exports.createResolver = function (options) {
unsafeCache,
resolver: customResolver,
restrictions,
roots
roots,
styleFields
} = normalizedOptions;

const plugins = userPlugins.slice();
Expand Down Expand Up @@ -507,6 +512,16 @@ exports.createResolver = function (options) {
)
);
});
// resolve-in-package
styleFields.forEach(styleField => {
plugins.push(
new StyleFieldPlugin(
"existing-directory",
styleField,
"resolve-in-existing-directory"
Comment on lines +519 to +521
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: check if these are the correct source and target

)
);
});
plugins.push(
new NextPlugin("resolve-in-package", "resolve-in-existing-directory")
);
Expand Down
95 changes: 95 additions & 0 deletions lib/StyleFieldPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Burhanuddin Udaipurwala @burhanuday
*/

"use strict";

const path = require("path");
const DescriptionFileUtils = require("./DescriptionFileUtils");

/** @typedef {import("./Resolver")} Resolver */
/** @typedef {import("./Resolver").JsonObject} JsonObject */
/** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */
/** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */

const alreadyTriedStyleField = Symbol("alreadyTriedStyleField");

module.exports = class StyleFieldPlugin {
/**
* @param {string | ResolveStepHook} source source
* @param {string} name fieldName
* @param {string | ResolveStepHook} target target
*/
constructor(source, name, target) {
this.source = source;
this.name = name;
this.target = target;
}

/**
* @param {Resolver} resolver the resolver
* @returns {void}
*/
apply(resolver) {
const target = resolver.ensureHook(this.target);
resolver
.getHook(this.source)
.tapAsync("StyleFieldPlugin", (request, resolveContext, callback) => {
// if request.context.issuer is not a css file, don't resolve to style field
const isIssuerAStyleFile =
request.context &&
// @ts-ignore
request.context.issuer &&
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: check if type can be improved

// @ts-ignore
request.context.issuer.endsWith(".css");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: check for all known css extensions
todo2: allow consumer to configure extensions array

if (
!isIssuerAStyleFile ||
request.path !== request.descriptionFileRoot ||
/** @type {ResolveRequest & { [alreadyTriedStyleField]?: string }} */
(request)[alreadyTriedStyleField] === request.descriptionFilePath ||
!request.descriptionFilePath
)
return callback();
const filename = path.basename(request.descriptionFilePath);
let stylePath =
/** @type {string|null|undefined} */
(
DescriptionFileUtils.getField(
/** @type {JsonObject} */ (request.descriptionFileData),
this.name
)
);

if (
!stylePath ||
typeof stylePath !== "string" ||
stylePath === "." ||
stylePath === "./"
) {
return callback();

Check warning on line 70 in lib/StyleFieldPlugin.js

View check run for this annotation

Codecov / codecov/patch

lib/StyleFieldPlugin.js#L70

Added line #L70 was not covered by tests
}
if (
// this.options.forceRelative
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: add support for forceRelative

true &&
!/^\.\.?\//.test(stylePath)
)
stylePath = "./" + stylePath;

Check warning on line 77 in lib/StyleFieldPlugin.js

View check run for this annotation

Codecov / codecov/patch

lib/StyleFieldPlugin.js#L77

Added line #L77 was not covered by tests
/** @type {ResolveRequest & { [alreadyTriedStyleField]?: string }} */
const obj = {
...request,
request: stylePath,
module: false,
directory: stylePath.endsWith("/"),
[alreadyTriedStyleField]: request.descriptionFilePath
};
return resolver.doResolve(
target,
obj,
"use " + stylePath + " from " + this.name + " in " + filename,
resolveContext,
callback
);
});
}
};
2 changes: 2 additions & 0 deletions test/fixtures/style/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import "./style.css";
import "style-library";
1 change: 1 addition & 0 deletions test/fixtures/style/node_modules/style-library/main.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions test/fixtures/style/node_modules/style-library/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions test/fixtures/style/node_modules/style-library/style.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions test/fixtures/style/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"main": "index.js"
}
1 change: 1 addition & 0 deletions test/fixtures/style/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "style-library";
57 changes: 57 additions & 0 deletions test/styleField.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const path = require("path");
const fs = require("fs");
const ResolverFactory = require("../lib/ResolverFactory");
const CachedInputFileSystem = require("../lib/CachedInputFileSystem");

const fixture = path.resolve(__dirname, "fixtures", "style");

describe("StyleFieldPlugin", () => {
const nodeFileSystem = new CachedInputFileSystem(fs, 4000);

const resolver = ResolverFactory.createResolver({
fileSystem: nodeFileSystem
});

it("should resolve style field", done => {
resolver.resolve(
{
issuer: path.resolve(fixture, "style.css")
},
fixture,
"style-library",
{},
(err, result) => {
console.log(
"🚀 ~ file: styleField.test.js:17 ~ resolver.resolve ~ err, result:",
err,
result
);
if (err) return done(err);
if (!result) return done(new Error("No result"));
expect(result).toEqual(
path.resolve(fixture, "node_modules/style-library/style.css")
);
done();
}
);
});

it("should not resolve to style field if request is not coming from a css file", done => {
resolver.resolve(
{
issuer: path.resolve(fixture, "index.js")
},
fixture,
"style-library",
{},
(err, result) => {
if (err) return done(err);
if (!result) return done(new Error("No result"));
expect(result).toEqual(
path.resolve(fixture, "node_modules/style-library/main.js")
);
done();
}
);
});
});