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

Importing Less files via plugins #2402

Closed
Justineo opened this issue Jan 21, 2015 · 17 comments · Fixed by #2429
Closed

Importing Less files via plugins #2402

Justineo opened this issue Jan 21, 2015 · 17 comments · Fixed by #2429

Comments

@Justineo
Copy link
Contributor

Hi,

Is there a way for Less plugins to import Less files before everything else while compilng?

Stylus seems to allow this kind of implicity import for a library.

@matthew-dean
Copy link
Member

Can you clarify what you mean by "Less files" and "everything else"?

@Justineo
Copy link
Contributor Author

I have a Less mixin library est, and I'd like to make a plugin version so that users won't need to explicitly do @import "est/[version]/src/all.less" and can still use mixins it provided.

For instance, it provides a mixin .clearfix(), users can directly use it in their style files:

.a {
  .clearfix();
}

Or add a simple @import "est" at the beginning to include the mixin library.

And when build with the corresponding plugin, it will be compiled correctly.

lessc style.less --est

If I use the programatic way I can append library file before actual input but I'd like lessc can do it too.

In Stylus it is possible to simply use stylus.import('...'). I checked Less's source code and parser.parse is not a prototype function and cannot be overwritten by plugins (or if it is, it will still be not appropriate to do that in plugins).

So I'm wondering if you guys would have some idea about this.

@seven-phases-max
Copy link
Member

So that in addition to installing and updating a library I will have to install and update a corresponding plugin? (It's not that I'm strictly against this purpose - I guess it may have some uses in certain cases) - but in this particular case: why don't you just rename your all.less to est.less and simplify path to it?
So normally it could be the same ("less" extension is implied automatically):

@import "est";

In your example it looks like you're just going to abuse the fact that plugins are installed into a predefined directory.


Related to #1241.

@Justineo
Copy link
Contributor Author

Thanks for ur reply Max.

What I'm trying to do is make a mixin library both importable from Less include path and can be injected by a plugin (like #1241).

It may have Less mixin files in src (which can be imported directly if you put the project in Less include path) and plugin files in lib (which injects files under src when using the plugin). So it won't be necessary to install an extra plugin.

I found that the plugin less-plugin-npm-import seem to be doing a similar job except it uses a npm:// prefix to import Less file from npm packages explicitly.

In my particular case, maybe I can implement a similar file manager but explicit @import statement seemed to be inevitable.

@lukeapage
Copy link
Member

you can use visitors to alter the ast before eval - but thats after the import phase.
if you are prepared you could parse your import yourself and then have a visitor that appends it to the root rules.

We could consider making a new plugin type but you would have to pr it - it would be as above but encapsulated. If you are interested i can give feedback.

@matthew-dean
Copy link
Member

Sounds like it may also be related to the proposed @import (plugin) "/js/myplugin.js"; syntax in #1861? That would allow a library to include a plugin without injecting it into the command-line statement, but it sounds like you may want the reverse, for a plugin to inject the library.

@Justineo
Copy link
Contributor Author

@lukeapage it seems that plugins can't access render options? So maybe it's not easy to implement using a visitor right now I think.

@lukeapage
Copy link
Member

plugins can access their own options - you can only access render options by getting into the render phase by creating a node that wraps another and intercepts generateCSS

@Justineo
Copy link
Contributor Author

@lukeapage I'm trying to parse the library code into a tree and prepend its rules to the root ruleset of user AST. How can I access context and options inside a visitor? They seem to be necessary for Parser.

@seven-phases-max
Copy link
Member

Can't plugin just concatenate/inject anything it needs to the compiled source string (before actual parsing)? (That is, instead of making either programmatic imports or merging parsed trees we can just inject the list of imports as plain text string (of valid Less code)). I realize that currently there's no dedicated function or any "official" interface for this (so it's a room for some hack) but would not this be just more simple and more generic? (i.e. the main point: "imports" vs. "anything").

@Justineo
Copy link
Contributor Author

@seven-phases-max yes it's a lot easier but it seem to be impossible to do that without touching Less source code. The input string is not exposed for any change except for global variables and banner actually. Prepending rules to the AST is the only way to inject library for now I think. I agree it's not a elegant way and I don't know how to access parsing context inside a visitor.

@matthew-dean
Copy link
Member

IIRC that shouldn't be impossible, because even in the examples on lesscss.org, it demonstrates rendering a LESS string into CSS output, so that string could in fact be a modified (prepended) source file, rather than a file reference. You should be able to either render a string or render a file.

@Justineo
Copy link
Contributor Author

@matthew-dean I can't do that because lessc doesn't expose any interface for that. Of course I can do it programatically but I want lessc to work as well.

@matthew-dean
Copy link
Member

Hmmm.... but... same idea, if your plugin added a custom file manager, then upon the first request, could you not return the prepended text + file? Not a perfect solution, but still seems un-impossible.

@Justineo
Copy link
Contributor Author

File manager only works when we have @import statements in our input file. The input file is not loaded by file manager so if we do not have imports in it the library won't be injected.

@lukeapage
Copy link
Member

We have post process plugins, it would be trivial to add a pre-process
plugin, i can look into that.

@Justineo
Copy link
Contributor Author

Justineo commented Feb 2, 2015

Just created a PR.

Plugin code looks like this:

Injector.prototype.process = function (src, extra) {
    var injected = '@import "all.less";\n';
    var ignored = extra.imports.contentsIgnoredChars;
    var fileInfo = extra.fileInfo;
    ignored[fileInfo.filename] = ignored[fileInfo.filename] || 0;
    ignored[fileInfo.filename] += injected.length;
    extra.context.paths.push(path.resolve(__dirname, '../src'));
    return injected + src;
};

Don't know if this is a proper way to inject library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants