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

create-crank-app #94

Open
dfabulich opened this issue May 4, 2020 · 13 comments
Open

create-crank-app #94

dfabulich opened this issue May 4, 2020 · 13 comments
Labels
documentation Improvements or additions to documentation enhancement New feature or request help wanted Extra attention is needed

Comments

@dfabulich
Copy link

Setting up JSX and a module bundler can be kinda tricky. If you create an npm project starting with create-, e.g. create-blah you can type npm init blah, which is equivalent to npx create-blah.

https://docs.npmjs.com/cli/init

npm init <initializer> can be used to set up a new or existing npm package.

initializer in this case is an npm package named create-<initializer>, which will be installed by npx, and then have its main bin executed – presumably creating or updating package.json and running any other initialization-related operations.

https://github.com/facebook/create-react-app is the most famous of these, and infamously has a ton of complicated features. I would also call attention to Stencil's initializer https://github.com/ionic-team/create-stencil which is still pretty complicated, but it's not as bad.

@dfabulich
Copy link
Author

I don't know where to put this in a PR (I think probably create-crank-app should live in a separate repo?) but here's a quicky script I threw together that seems to do something useful.

import { mkdir, writeFile } from 'fs/promises';
import { fileURLToPath } from 'url';
import { dirname, basename } from 'path';
import { spawn } from 'child_process';


function die(error) {
  console.error(error);
  process.exit(1);
}

process.on('unhandledRejection', die);

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packageName = basename(__dirname);

const packageJson = {
  name: packageName,
  version: "1.0.0",
}

async function writeJson(filename, obj) {
  return writeFile(filename, JSON.stringify(obj, null, 2) + "\n", 'utf8');
}

async function spawnPromise(cmd) {
  const child = spawn(cmd, {shell: true});
  for (const pipe of ["stdout", "stderr"]) {
    child[pipe].setEncoding('utf8');
    child[pipe].on('data', data => process[pipe].write(data));
  }
  return new Promise((resolve, reject) => {
    child.on('close', code => {
      code ? reject() : resolve();
    });
  });
}

const html = `<!doctype html>
<html>
<head>
  <meta charset='utf-8'>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
  <script src="index.js"></script>
</body>
</html>
`;

const js = `/** @jsx createElement */
import {createElement} from "@bikeshaving/crank";
import {renderer} from "@bikeshaving/crank/dom";

function Greeting({name = "World"}) {
  return (
    <div>Hello {name}</div>
  );
}

renderer.render(<Greeting />, document.body);
`

const gitignore = `.cache
dist
node_modules
`;

void async function() {
  await writeJson('package.json', {
    name: packageName,
    version: '1.0.0',
    scripts: {
      start: "parcel index.html --open",
      build: "parcel build index.html",
    }
  });
  await spawnPromise('npm install @bikeshaving/crank');
  await spawnPromise('npm install --save-dev parcel-bundler');
  await Promise.all([
    writeJson('.babelrc', {presets:['@babel/preset-react']}),
    writeFile('index.html', html, 'utf8'),
    writeFile('index.js', js, 'utf8'),
    writeFile('.gitignore', gitignore, 'utf8'),
  ]);
  await spawnPromise('npm run build');
  console.log("Now you can `npm run start` to view your Crank app in a browser.");
}();

@dfabulich
Copy link
Author

dfabulich commented May 4, 2020

Possible enhancements:

  1. I think it's typical for initializers to accept a command-line argument for a directory to create? (I'm hazy on this; I can't see how to npm init my script without publishing an actual package to npm.)
  2. The current script clobbers any package.json file in the current directory, which seems unfriendly. (I'm not sure how other npm initializers handle this.)
  3. npm init typically interactively prompt the user for package.json fields, and offer a --yes -y CLI arg to accept all defaults
  4. This script leaves description and license blank, which causes npm install to print warnings
  5. The log is pretty noisy as the initializer calls npm install on a bunch of stuff. Some initializers hide that behind fancy progress bars. (But, on the other hand, that means you can't see the logs.)
  6. This script lets Parcel install Babel 7. Should it install Babel itself? (I kinda think no, but I'm not sure.)

@nykula
Copy link

nykula commented May 4, 2020

IIRC npm init foo bar is equivalent to node /path/to/create-foo bar? Entry point is the "bin" property in /path/to/create-foo/package.json, forwarding the arguments unchanged. Like, process.argv[2] should contain the dirname, and you fall back to process.cwd() if it's undefined.

What do you win by using pipes and async apis in a one-off script, if you don't modify output or parallelize commands?

@dfabulich
Copy link
Author

What do you win by using pipes and async apis in a one-off script, if you don't modify output or parallelize commands?

I do parallelize some of the commands, though, admittedly, not any of the spawns. (I thought I was going to, and then decided not to.)

@dfabulich
Copy link
Author

I figured out how to test npm init without pushing a release. I made a create-crank-app directory locally. Then I could do this:

cd ..
mkdir cca
cd cca
npm init -y
npm install ../create-crank-app
mkdir test
cd test
npm init crank-app

npm init crank-app, is indeed equivalent to npx create-crank-app, which found create-crank-app in the parent directory's node_modules.

npm init crank-app subtest did not, by itself, create a subdirectory; it just passed the arg to my script, indicating that we do have to do this by hand.

Nonetheless, I think the next step is for there to be a github repo for create-crank-app. Then we can turn my list of enhancements into issues, fix some/all of them, deploy to npm, and then update the documentation to recommend using it.

@ryhinchey
Copy link
Contributor

Preact cli does more than we need to out of the box (especially for the first iteration of this) but it's a great place to look for inspiration:

They keep their cli templates here

@brainkim brainkim added enhancement New feature or request documentation Improvements or additions to documentation help wanted Extra attention is needed labels May 5, 2020
@brainkim
Copy link
Member

brainkim commented May 5, 2020

I’m investigating snowpack and think it could potentially be revolutionary. I’ve been dissatisfied with all the major bundlers and the features/philosophical clarity of snowpack version 2 is something I admire.

https://www.snowpack.dev/#get-started

@dfabulich
Copy link
Author

Snowpack looks really cool, but Snowpack 2 is not in a good place right now. I couldn't get even the most basic Babel/JSX transpilation to work this afternoon. FredKSchott/snowpack#294

@brainkim
Copy link
Member

brainkim commented May 6, 2020

@dfabulich Yes as I investigate it seems like it uses both rollup and parcel under the hood somehow? 😨

@brainkim
Copy link
Member

brainkim commented May 6, 2020

@dfabulich do you enjoy finding build/environment bugs because you seem to be very good at it haha.

@ryhinchey
Copy link
Contributor

every time I go to try a new bundler/build tool in the JS world I always end up going back to webpack for bundling applications (js + css + html + assets). they have a few sane defaults and if you keep the config simple, it's not horrible and will do what you expect.

I'd highly suggest we use webpack for create-crank-app :)

@brainkim
Copy link
Member

brainkim commented May 6, 2020

Apparently the Vue.js Vite project supports preact as well. https://twitter.com/youyuxi/status/1257882133177274369
Screen Shot 2020-05-06 at 12 06 56 AM

I really do like the idea of no transpilation for development. I can never ever seem to get sourcemaps/errors working 100% and it seems like it could be a lovely development experience.

@ryhinchey If you snoop around https://github.com/bikeshaving/crank/blob/master/website/webpack.tsx?ts=2 you’ll see that I sorta attempted to create a bundler API which is defined entirely with components. The fact that components on the server can be async today means we can do stuff like this, and a lot of bundler configuration complexity stems from trying to get scripts to match the bundler output. If there was a bundler that had a clear and promise-fluent API which was like... you pass in a local file path and it compiles it for you and it spits out information about the bundle, we could actually get a sort of zero-config type of bundling system where you can just use enhanced versions of script and style tags to both kick off rendering and render html on the server.

@kstewart83
Copy link

kstewart83 commented Jun 15, 2020

I'm not sure if the complications described above came from trying to leverage Babel, but I was able to get the basics going with Snowpack 2 and just using the Typescript TSX compiler. Here is the gist with relevant app files and Snowpack configuration. I started with the Snowpack blank template.

The only thing that doesn't work is HMR. The HMR triggers, but module code is not refreshed. Looking at some of the plugins, it looks like each framework as their own way to register modules with Snowpack. I'm new to looking into the HMR internals. But it would seem that some sort of HMR plugin would need to be built specific to Crank. These plugins don't seem overly complex to write, but they are not trivial. I'll keep looking into it and see if how hard it would be to create a Snowpack plugin for managing the HMR process.

Fast React issue which describes the Fast React implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

5 participants