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

(potential intent to rfc): ember apply #783

Closed
NullVoxPopuli opened this issue Jan 5, 2022 · 14 comments
Closed

(potential intent to rfc): ember apply #783

NullVoxPopuli opened this issue Jan 5, 2022 · 14 comments

Comments

@NullVoxPopuli
Copy link
Contributor

NullVoxPopuli commented Jan 5, 2022

I kinda what to see how folks feel about this before I write anything up, but during discussions with folks on adding fastboot-ability in the ember-cli blueprints (opt-in, etc etc), the possibility of applying features incrementally came up. such as ember apply ssr. This is similar to what we've historically used install-blueprints for, but without the developers needing to know what packages to install. This would formally adopt a "default way" to do certain things -- and I want to emphasize default, because it's not to totally replace existing ways of doing things. For example, should I write an RFC for this, an example would be:

ember apply tailwind

and would setup tailwind with some default / easy way of interacting with tailwind (my preference with be either this or this).

Goals:

  • provide a default / easy / beginner-friendly way to "get going quickly"
  • powered by codemods (jscodeshift, template-recast), so that we can apply features after an app has already been made
  • use a combination of community addons and/or broader ecosystem-documentation to power the features that can be applied (tailwind is a good example of this (from the couple links above))
  • provide flags for applying integrations during ember new

Non-Goals:

  • supporting apply for addons (or instead we'd setup which integrations are for apps and which for addons)
  • implementing any specific integration during this RFC process (tailwind, ssr, sass, maybe pwa, etc would have to be separate RFCs to discuss trade-offs with different "default" configurations)

So, what's an example of this look like?

Using the example ember apply tailwind (since I'm very familiar with this)

tldr:

  • a script is run that adds deps, files, and changes existing files

Why:

  • tailwind 3 kinda just came out
  • tailwind JIT is great for reducing CSS impact
  • ember-cli-postcss, while a great addon for using postcss, is not currently viable for use with tailwind and JIT
  • avoiding the style pipeline as a beginner is an attractive option
  • we can make things "look simple, yet powerful and flexible" (also great for beginners)
  • docs for tailwind anywhere on the web work for ember using this approach (as long as they also use tailwind-cli)

Details:
This is kind of an outline with semi-psuedo code, because the implementation details would require some additional APIs / tooling that would be a part of this apply RFC in order to make ember apply tailwind's implementation nice

When ember apply tailwind runs:
(it automates the tailwind setup docs)

  • add dependencies:

    • autoprefixer @ ^10
    • postcss @ ^8
    • tailwindcss @ ^3
  • add new files:

    • await addFile('tailwind.config.js', content);

      tailwind.config.js
      'use strict';
      
      const appRoot = __dirname;
      
      module.exports = {
        content: [`${appRoot}/app/**/*.{js,ts,hbs}`, `${appRoot}/public/**/*.md`],
        theme: {
          extend: {  },
        },
        // plugins: [require('@tailwindcss/typography')],
      };
    • await addFile('postcss.config.js', content);

      postcss.config.js
      'use strict';
      
      module.exports = {
        plugins: {
          tailwindcss: {},
          autoprefixer: {},
        }
      }
    • await addFile('tailwind-input.css', content);

      tailwind-input.css
      @tailwind base;
      @tailwind components;
      @tailwind utilities;
  • add scripts to package.json

    • await addScript(tailwind:build, 'tailwindcss -i tailwind-input.css -o public/assets/tailwind.css');
      • for production
      • public gets copied to ember's default output directory, regardless of how it's configured
    • await addScript('tailwind:watch', 'tailwindcss -i tailwind-input.css -o public/assets/tailwind.css --watch');
      • for development
      • any changes to the public folder cause the ember app to update
  • add to the .gitignore public/tailwind.css, since it is a generated file and can
    change frequently

    • await gitIgnore('# compiled output', 'public/assets/tailwind.css');
  • link the tailwind output in the html files so that the app loads it -- example

    insertHTML(
      'app/index.html', 'head'
      `<link integrity="" rel="stylesheet" href="{{rootURL}}assets/tailwind.css">`
    )
    • same for the tests/index.html

When --tailwind is added to ember new my-app

  • the same steps as above happen, but after the regular ember new stuff finishes, but before the output steps (cd, ember s printed to the console)

Thoughts?

@chriskrycho
Copy link
Contributor

This is an interesting idea, but I'll note that it doesn't need to be integrated into Ember core at all. Since addons can supply commands, you could simply write ember-cli-apply and have it register the apply command and build out an ecosystem around this.

@NullVoxPopuli
Copy link
Contributor Author

oh, I like that idea.
then, once we have sufficient integrations to be useful to the masses, we can RFC to adopt ember-cli-apply as included in default.

however, that prevents the possibility of --ssr and friends during ember new 🤔

@chriskrycho
Copy link
Contributor

It already has support for applying blueprints, I believe? It’s just a longer form than people typically type.

@rwjblue
Copy link
Member

rwjblue commented Jan 5, 2022

A few things that jump out at me on first read:

  • It's not obvious that this is better than providing a nice structure for folks to use to make their own npx tailwind commands (somewhat similar to the original goal of npx @ember/octanify)
  • Where would the actual code live? How do we make sure to decouple what to do to get SSR or tailwind integration setup with various combinations of dependencies?

@gossi
Copy link

gossi commented Jan 6, 2022

I like this idea. The name for apply sounds like a recipe name and ember apply cooks takes the ingredients and applies the "instructions". So ember apply <recipe-name>. Each recipe then can be it's own addon (which is installed on demand (?)), for example ember-cli-apply-ssr - similar to what ember-cli-deploy is doing (would answer rob's question).

Could imagine something like this to exist. I wouldn't need this to be part of ember new (from the get go). If this has matured and adopted, an RFC to ship this as default seems to be a good approach and then this can reach at a deeper integration, such as ember new --apply=ssr,tailwind.

@Windvis
Copy link

Windvis commented Jan 6, 2022

I only took a quick look but this seems similar to preset. It might be better to go for a framework agnostic solution (if possible) instead of creating something custom for Ember projects?

@NullVoxPopuli
Copy link
Contributor Author

NullVoxPopuli commented Jan 6, 2022

Oh legit, i hadn't heard of preset. Gonna prototype with that.
It's all text based, so no codemods, it seems. But it should get basics going. And since it just runs a script, i can keep all the utilities i made yesterday

@simonihmig
Copy link
Contributor

simonihmig commented Jan 6, 2022

The first thing that jumped to my mind was how is this actually different from a regular blueprint? You can do basically all the things you listed above in a blueprint. For some there is built-in support (like adding addons or regular npm packages), for some you would have to write custom node code run from e.g. the afterInstall hook.

For cases where you would like better high level support, like adding AST-based updates to existing files (e.g. things like what ember-router-generator provides), this would be useful for both blueprints that run as part of installing an addon (what we have now) and when applying recipes. So I wonder if this shouldn't be built on top of the same infrastructure we already have?

So ember apply . Each recipe then can be it's own addon

Yeah, when a recipe is basically an addon, you don't need a centralized place to store recipes, everybody can create recipes. But then even more the question arises what makes a recipe special compared to a regular addon (that has a default blueprint).

It seems to me the only thing that is fundamentally different is that when the recipe is really just a "meta addon" (it itself does not have any build-time or runtime code, or dependencies that are need for the app), then you don't want to have it added to your package.json. In that case, ember apply foo could be almost the same as ember install foo, i.e. it downloads foo (or ember-cli-apply-foo?) and runs its default blueprint, it just doesn't add it as a dependency.

Again, all the other possible convenience features like codemods etc. either have to be done manually (as of today), or eventually become features of blueprints in general, regardless of whether this is apply or install.

Does that make sense?

@NullVoxPopuli
Copy link
Contributor Author

Does that make sense?

it does, I mostly find the blueprint APIs immensely inconvenient to use.
Everything I want to do with the existing system "feels like a hack" :(

@mehulkar
Copy link
Contributor

mehulkar commented Jan 6, 2022

One thing I've heard (but not personally verified) is that the existence of every addon makes ember commands slower because ember-cli has to traverse node_modules to find all possible commands (and their overrides).

Replacing included commands a single apply command to obviate the need for this discovery is a good idea. An additional thing I'd like is for addons packages to explicitly register themselves with ember-cli, rather than ember-cli having to traverse and discover things that can be "applied". I'm not sure what the mechanism would be (probably would have to be somewhat intertwined with ember install?), but that would be a huge improvement.

@NullVoxPopuli
Copy link
Contributor Author

NullVoxPopuli commented Jan 6, 2022

I like this API better than the blueprint api:

export default async function run(workingDirectory) {
  await addDevDependencies({
    autoprefixer: '^10.0.0',
    postcss: '^8.0.0',
    tailwindcss: '^3.0.0',
  });

  await applyFolder(path.join(__dirname, 'files'));
  await addHTML(
    'app/index.html',
    `<link integrity="" rel="stylesheet" href="{{rootURL}}assets/tailwind.css">`,
    { before: 'link' }
  );
  await addHTML(
    'tests/index.html',
    `<link rel="stylesheet" href="{{rootURL}}assets/tailwind.css">`,
    { before: 'link' }
  );

  await addScripts({
    'tailwind:build': 'npx tailwindcss -i ./tailwind-input.css -o ./public/assets/tailwind.css',
    'tailwind:watch':
      'npx tailwindcss -i ./tailwind-input.css -o ./public/assets/tailwind.css --watch',
    build: 'npm run tailwind:build && ember build --environment=production',
  });

  await gitIgnore('public/assets/tailwind.css', '# compiled output');
}

easier to debug, imo.
(I almost have this fully working)

today, blueprints only really do:

  • addDevDependenices (or addDependencies?)
  • applyFolder (deep recursive copy)

but what I don't have (and maybe don't care for?) is the parameterization via ERB?)

also, dat speed:

time node ../../NullVoxPopuli/ember-apply/src/cli/index.js tailwind

real	0m0.180s
user	0m0.198s
sys	0m0.028s

@NullVoxPopuli
Copy link
Contributor Author

NullVoxPopuli commented Jan 7, 2022

this seems similar to preset.

Just tried integrating my stuff with preset.
Unfortunately, it's a non-starter:

  • requires esbuild (for some reason? probably because the plugins are all uncompiled typescript)
    • discovered due to esbuild not installing because I have ignore-scripts set to true for safety (I think everyone should have ignore-scripts).
  • tries to run npm, even if in a yarn project (even though I didn't tell it to install dependencies).

obvs, these things can be addressed, but I think we can do better / simpler

@sandstrom
Copy link
Contributor

sandstrom commented Jan 29, 2022

In my personal opinion, it sounds like this would be easier to manage at the documentation level.

Basically add a recipes section to the docs. There are always going to be gnarly stuff, and writing + maintains this in code, instead of text, will be more work.

Also, the threshold for contributions is higher. Anyone can PR doc-changes, far fewer people are skilled (and read-in) enough to contribute code changes.

@NullVoxPopuli
Copy link
Contributor Author

I think that makes sense.

Code can always mirror documentation better than the other way around.

Gonna close this since https://github.com/NullVoxPopuli/ember-apply
Exists in user space, and can automate documentation instructions.

Thanks all!

Excited for the cookbook RFC!

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

No branches or pull requests

8 participants