Giter VIP home page Giter VIP logo

rna's Introduction

RNA logo


RNA is a build framework

We built RNA to be pluggable and to be interoperable with other build systems. A lot of esbuild and postcss plugins are distribuited as standalone packages in order to be reused outside the RNA opinionated ecosystem. We also designed a micro-sdk for esbuild plugin authors that handles transform pipelines and emits chunks or files.

RNA is a bundler

RNA bundler is heavily based on esbuild, an extremely fast JavaScript bundler with some pre-configured addons. It can bundle and optimize JavaScript, TypeScript, JSX, CSS and HTML and collect referenced assets just using languages features.

The bundler is designed for modern browsers, but it can transpile code for IE11 and other legacy browsers with Babel and PostCSS plugins.

RNA is a dev server

Build plugins are also available for the Web Dev Server. Since both WDS and RNA aim to use standard syntax and practises in web projects, you can run a local server with hot module replacement and CSS livereload without have to bundle your web app first or to re-run a partial build for each change. Files loaded via ESM will pass through a little esbuild transpilation in order to support TypeScript, CommonJS modules and node resolution, making a great difference in developer experience. The dev server can be used also for PHP with an Encore-like approach.

RNA is a cli

Quick usage

npm i -D @chialab/rna

package.json

{
    "scripts": {
        "start": "rna serve src --port 3000",
        "build": "rna build src/index.html --output public"
    }
}

Tutorials


License

RNA is released under the MIT license.

rna's People

Contributors

bashmish avatar cowwoc avatar edoardocavazza avatar github-actions[bot] avatar le0m avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

rna's Issues

Can we have assets in a separate directory to the main html file?

So my previous webpack config would generate something which looked like this. html-webpack-plugin

public
β”œβ”€β”€ index.html
└── ui-assets
    β”œβ”€β”€ assets-manifest.json
    β”œβ”€β”€ main.d2fe0910b8.js
    β”œβ”€β”€ main.d2fe0910b8.js.map
    β”œβ”€β”€ vendor.67dfc8f6a1.js
    └── vendor.67dfc8f6a1.js.map

This was useful to us because we didn't want the main entry point (index.html) to be hashed. it also allowed us to serve static assets from a different place.

@chialab/[email protected] generates this:

public
β”œβ”€β”€ 1-25MTWOSN.js
β”œβ”€β”€ 1-25MTWOSN.js.map
β”œβ”€β”€ 1-LCOOVKTY.css
β”œβ”€β”€ 1-LCOOVKTY.css.map
└── index-LJMJ3LED.html

We don't want the main entry point hashed, and we would like the assets (JS and css files) to be in a separate directory.

Is this possible for you to implement?

bad path separator for assets

Under windows, my html source code is this:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Demo</title>
        <link href="./app.less" rel="stylesheet">
        <script type="module" src="./app.ts"></script>
    </head>
    <body>
    </body>
</html>

I run esbuild by code, the config is :

{
   //...
    assetNames: 'assets/[name]',
    chunkNames: 'assets/[name]',
   //...
}

the resulting html is:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>Demo</title>
    <link href="assets\app.css" rel="stylesheet">
    <script src="assets\app.js" type="module"></script>
</head>

<body>
</body>

</html>

As you can see, the link & scripts sources contains a \ instead of /

`entryNames: '[dir]/[name]'` breaks plugin-html

this line in my esbuild config... entryNames: '[dir]/[name]',
along with the html plugin... gets me this exception:


✘ [ERROR] [plugin html] ENOENT: no such file or directory, copyfile 'proj/packages/viewer/dist/assets/index-LCFOZCXA.html' -> 'proj/packages/viewer/dist/[dir]/index.html'

  This error came from the "onEnd" callback registered here:

    proj/node_modules/esbuild/lib/main.js:814:22:
      814 β”‚         let promise = setup({
          β•΅                       ^

    at setup (file://proj/node_modules/@chialab/esbuild-plugin-html/dist/index.js:60:162521)
    at handlePlugins (proj/node_modules/esbuild/lib/main.js:814:23)
    at Object.buildOrServe (proj/node_modules/esbuild/lib/main.js:1108:7)
    at proj/node_modules/esbuild/lib/main.js:1961:17
    at new Promise (<anonymous>)
    at Object.build (proj/node_modules/esbuild/lib/main.js:1960:14)
    at Object.build (proj/node_modules/esbuild/lib/main.js:1814:51)
    at main (proj/scripts/build-app.js:147:32)

Error: Build failed with 1 error:
error: ENOENT: no such file or directory, copyfile 'proj/packages/viewer/dist/assets/index-LCFOZCXA.html' -> 'proj/packages/viewer/dist/[dir]/index.html'
    at failureErrorWithLog (proj/node_modules/esbuild/lib/main.js:1557:15)
    at proj/node_modules/esbuild/lib/main.js:1215:28
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  errors: [
    {
      pluginName: 'html',
      text: "ENOENT: no such file or directory, copyfile 'proj/packages/viewer/dist/assets/index-LCFOZCXA.html' -> 'proj/packages/viewer/dist/[dir]/index.html'",
      location: null,
      notes: [Array],
      detail: [Error]
    }
  ],
  warnings: []
}

clearly the [dir]/ bit isn't being resolved.

interestingly, [dir]/ in `assetNames works fine, though... it does lead to some weird directory structures.. :)
image

Style urls resolutions in esbuild

Currently we use postCSS to resolve url references for both bundler and server. We could use the builtin esbuild resolve hook instead, in order to completely avoid postcss parsing when not required.

Handle plugin dependencies

Right now, the RNA ecosystem of esbuild plugins has a lot of interdependencies. Some plugin explicitly depends on other, so we need to ensure that all the plugin chain is respected when a plugin is setting up.

Engines constraints

Add node engines constraints to package.json files with the minimum required node version with esm support.

Wrong path for CSS assets

For the CSS assets generated by esbuild (included in the metafile, not as a ), the generated file path appears platform specific. When run on Windows, the path is a single backslash, which is not the standard (forward slash).
In the worst case, a path given to the "loadStyle" function is e.g. "static\index-23423.css", in which the single backslash actually produces a completely wrong path, preventing CSS from rendering in the browser.

Please always use forward slashes as path separators in the generated HTML.

By the way, why are CSS assets from the metafile included es loader scripts instead of a simple <link ...>?

The problem is in https://github.com/chialab/rna/blob/main/packages/esbuild-plugin-html/lib/collectScripts.js
Lines 110-111.

[esbuild-plugin-html] Hash isn't based on file contents.

The hash only depends on the import statements, not on the contents of the file.
It's not possible to use this for browser caching since editing the imported scripts doesn't update the output hash.

See here for an example: (the same issue is present for CSS)

const contents = elements.map((element) => {
if ($(element).attr('src')) {
return `import './${$(element).attr('src')}';`;
}
return $(element).html();
}).join('\n');
const hash = crypto.createHash('sha1');
hash.update(contents);
return {
build: {
entryPoint: `index.${hash.digest('hex').substr(0, 8)}.${format}.js`,

I'm wondering if you could instead specify the entryNames and let esbuild calculate the hash.
I don't think the finisher has to do anything different, since it already gets the entrypoint by position and not file name.

Provide helpers for esbuild results merging

Plugins like esbuild-plugin-html and rna-storybook create files in the output dir in a separated process (eg, directly using the fs). We need to provide a better way to reconcile output manifests and watched files.

Encore support

Make build and serve command compatible with Encore using esbuild manifest.

[esbuild-html-plugin] - Assets are loaded into the [dir] directory

Use case:

I am trying to build multiple HTML files such that all entries are populated with the same directory structure as my source folder.

Attempt:

I used the following build configuration in my esbuild.config.js file:

import esbuild from "esbuild";
import htmlPlugin from "@chialab/esbuild-plugin-html";

await esbuild.build({
  entryPoints: ["src/index.html","src/sub/index.html"],
  entryNames: '[dir]/[name]',
  outdir: "public",
  outbase: "src",
  plugins: [htmlPlugin()],
  assetNames: "[dir]/[name]",
  bundle: true,
  minify: true,
  write: true,
});

Expected result:

Assets are loaded into an "assets" folder, and the HTML file directories are preserved in the out folder.

Further, I would expect sub/sub.html's css file to resolve to ../css/whatever.css rather than css/whatever.css. The latter occurs, resulting in a 404 response for the css fetch.

Observed result:

  1. Assets are loaded into the folder "[dir]".

image

  1. Css references in subdirectories are not relative to the outputted css folder.

[esbuild-plugin-meta-url] has no effect

I recently started a project which tries to implement a map renderer in Rust. I'm currently evaluating which bundler is the best for the task as I require two features:

  • Inlining webworkers (or any module)
  • Resolving/Handling import.meta.url in CJS and ESM modules, especially for .wasm files.

I tried the plugin @chialab/esbuild-plugin-meta-url to solve the second goal, but esbuild is not copying assets to the output directory.

Config:

build.mjs

import esbuild from 'esbuild';
import metaUrlPlugin from '@chialab/esbuild-plugin-meta-url';

await esbuild.build({
    entryPoints: ['src/index.ts'],
    bundle: true,
    format: "esm",
    platform: "browser",
    outdir: "dist/esbuild/",
    plugins: [
        metaUrlPlugin(),
    ],
});

Is there more configuration needed?

[esbuild-plugin-html] Load style files without script

When using the HTML plugin and I import styles in JS via

import "path/to/style.css";

it results in an additional script tag added to the HTML:

<script type="module">
        (function() {
            function loadStyle(url) {
                var l = document.createElement('link');
                l.rel = 'stylesheet';
                l.href = url;
                document.head.appendChild(l);
            }
            loadStyle('style.css');
        }());
</script>

Not sure what the reason for this is over just including it via a <link> tag directly, perhaps to avoid render blocking?
Anyway I would like to have at least an option to configure it s.t. this would just add

<link rel="stylesheet" href="style.css"/>

because I want my styles to be render blocking (to avoid FOUC) and save some bytes.

The code responsible for this seems to be in collectScripts.js.
Happy to provide the PR for this change myself if you agree with this proposal.

Thanks for this plugin btw :)

esbuild-plugin-html does not support font file ttf

Perhaps I'm overlooking an existing option? My expectation is that this would just be copied over to the assets build folder, just like any other asset.

src/client/styles/main.css:8:11: error: No loader is configured for ".ttf" files: src/client/fonts/VT323.ttf
    8 β”‚   src: url('../fonts/VT323.ttf') format('truetype');

[esbuild-plugin-html] Assets loaded via file-loader in the wrong directory

I have a component requiring an image that is processed via ESBuild's file loader.

cf. https://github.com/martpie/museeks/blob/b7e644ac1bf8bf383cbbde6fa6b2f32326673aac/esbuild.mjs#L87-L92

It would seem those assets are then placed in the JS build directory instead of the root of the dist folder:

Screenshot 2021-12-30 at 18 42 21

but ESBuild translates the URL to ./placeholder.png, which is of course not resolvable from app.html (at the root) and triggers a 404.

Sidenote: maybe even CSS should be at the root.

Can't use assets in a SPA without proxy

The problem is with the esbuild-html-plugin.
I have a script defined in my HTML entry point like this:

<script src="index.js></script>

The means that it gets transformed to something like:

<script src="index-234234.js></script>

If I use my app as a SPA, this script path is appended to the app's URL, like:
https://example.com/some/path/index-23434.js
whereas it should be in the root folder: https://example.com/index-23434.js
Currently the only way out is to use a proxy, which I don't want to.

The plugin should allow setting paths to assets like this:

<script src="/index.js></script> (with the forward slash, indicating that whatever name it eventually gets, it's always available from the root)

Currently the plugin just ignores such assets.

React example? To replace react-scripts build

I've been trying to produce a valid React build first with esbuild and then with rna, without much success.

"react": "^17.0.2",
"react-dom": "^17.0.2",

The furthest I got is this esbuild command:

esbuild src/index.jsx --bundle --outdir=esbuild/ --loader:.js=jsx --minify --chunk-names=chunks/[name]-[hash] --entry-names=[dir]/[name]-[hash]

It generates two files, js and css, content hashed.

image

Next step would be including this in html, which is where my struggle began. I tried your HTML plugin for esbuild, but it did not generate correct JS output, only small files a few dozen bytes in size.

Now with RNA CLI:

"build": "yarn rna build src/index.html --output rna",

"devDependencies": {
  "@chialab/rna": "^0.15.3",
  "@chialab/rna-bundler": "^0.15.16"
}

It produced the following JS, a bunch of PNGs, index.html and manifest.json, none of the files are content hashed.

image

And linked js.map from HTML where JS was linked (what?!). Also no content hash for assets, so can't work in production.

image

Do you have an example of building a standard React application built with react-scripts?

"build": "react-scripts build",

esbuild and the ecosystem around it seems to have a steep learning curve, and figuring it out with google, Stack Overflow or
official docs is not an option. All online tutorials I found, in text or video format, do not have content hashing for assets, they just copy the HTML as is.

Invalidate pre-bundled dependencies

Right now, The RNA dev server pre-bundles node_modules dependencies on request. We should watch dependency files anyway and then invalidate the bundle.

[esbuild-plugin-html] Wrong script src when using sourcemap

I have a problem when using the esbuild-plugin-html (0.15.9) with esbuild option sourcemap enabled.
This results in as html file that references the .js.map file instead of the desired .js file.

Excerpt of esbuild.config.js file:

import esbuild from 'esbuild';
import htmlPlugin from '@chialab/esbuild-plugin-html';

esbuild.build({
	entryPoints: [ './index.html' ],
	plugins: [ htmlPlugin() ],
	bundle: true,
	sourcemap: true,
});

Content of the index.html file:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<script src="./index.ts"></script>
</head>
<body>
	<div id="app"></div>
</body>
</html>

Excerpt of output index.html file:
<script src="1.js.map" type="application/javascript"></script>
Desired output:
<script src="1.js" type="application/javascript"></script>

Possible fix in file esbuild-plugin-html/lib/collectScripts.js

  • line 41:
    const jsOutput = files.find((file) => file.endsWith('.js'));
  • line 52:
    const cssOutputs = files.filter((file) => file.endsWith('.css'));

Support ESBuild in serve mode

When esbuild runs in serve mode it builds and references the main index.html in memory.

this means the relative files it refers to don’t work properly.

here is an example of an error:

✘ [ERROR] [plugin html] Build failed with 1 error:
src/2.js:1:7: ERROR: Could not resolve "./ui-assets/1-7YBC7F6V.js"

  This error came from the "onLoad" callback registered here:

    ../node_modules/esbuild/lib/main.js:845:22:
      845 β”‚         let promise = setup({
          β•΅                       ^

    at onTransform (file:///../node_modules/@chialab/esbuild-rna/lib/index.js:469:20)
    at setup (file:///../node_modules/@chialab/esbuild-plugin-html/dist/index.js:61:162844)
    at handlePlugins (/../node_modules/esbuild/lib/main.js:845:23)
    at Object.buildOrServe (/../node_modules/esbuild/lib/main.js:1139:7)
    at /../node_modules/esbuild/lib/main.js:2080:17
    at new Promise (<anonymous>)
    at Object.build (/../node_modules/esbuild/lib/main.js:2079:14)
    at Object.build (/../node_modules/esbuild/lib/main.js:1929:51)
    at file:///../cms/build.mjs:97:9

Error: Build failed with 1 error:
error: Build failed with 1 error:
src/2.js:1:7: ERROR: Could not resolve "./ui-assets/1-7YBC7F6V.js"
    at failureErrorWithLog (/../node_modules/esbuild/lib/main.js:1605:15)
    at /../node_modules/esbuild/lib/main.js:1251:28
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  errors: [
    {
      detail: [Error],
      location: null,
      notes: [Array],
      pluginName: 'html',
      text: 'Build failed with 1 error:\n' +
        'src/2.js:1:7: ERROR: Could not resolve "./ui-assets/1-7YBC7F6V.js"'
    }
  ],
  warnings: []
}

one solution I’ve seen is to accept a new option serve, which will use data URLs for assets instead of generating files.

This may be a workaround until esbuild provides better support.

More info:

Option to specify output folder name for @chialab/esbuild-plugin-html

Hey, great plugin.

There's just one issue for us, currently the exported folders are hardcoded to iife or esm.
We need our assets folder to have a specific name, is it possible to offer an override for this?

So instead of dist/iife/index.js we can have dist/assets/index.js (plus same in the index.html also)

html plugin

Hi guys,

I am currently trying the html plugi. It looks promising but currently I am facing 2 issues:

  1. It doesn't like path starting with /, it looks in my own / dir. It would be fine if we could have a root dir in options, or maybe even root dirs
  2. It doesn't like path starting with http:// looking in my own dir too

esbuild-plugin-commonjs not transform commonjs to esm

I was trying to convert react to ESM so that browser can consume it as a native ESM module.

Tested with [email protected]

Here is esbuild setup:

import { build } from "esbuild";
import commonjsPlugin from "@chialab/esbuild-plugin-commonjs";

await build({
  entryPoints: [
    "./node_modules/react/index.js",
  ],
  bundle: true,
  outfile: "out.js",
  format: "esm",
  loader: {
    ".js": "js",
  },
  plugins: [commonjsPlugin()],
})

The output is still commonjs module style while I was expecting module.exports to be converted to ESM export statements

html plugin unexpected output with `outdir` option

Here's my folder structure:

package.json
{
  "name": "esbuild-html",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "type": "module",
  "devDependencies": {
    "@chialab/esbuild-plugin-html": "^0.11.19",
    "esbuild": "^0.12.20"
  }
}
src/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Home</title>
  </head>
  <body>
    <h1>Home</h1>
    <script src="index.js" type="module"></script>
  </body>
</html>
src/index.js
console.log('Home')
export {}
src/sub/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Sub</title>
  </head>
  <body>
    <h1>Sub</h1>
    <script src="index.js" type="module"></script>
  </body>
</html>
src/sub/index.js
console.log('Sub')
export {}
build.js
import esbuild from 'esbuild'
import htmlPlugin from '@chialab/esbuild-plugin-html'

esbuild.build({
  entryPoints: ['./src/index.html', './src/sub/index.html'],
  outdir: './dist/',
  plugins: [htmlPlugin({})]
})

When I run node build.js I expect the resulting dist directory to look something like this:

dist/
β”œβ”€ index.html
β”œβ”€ index.js
└─ sub/
   β”œβ”€ index.html
   └─ index.js

What I get instead is:

dist/
β”œβ”€ index-[hash1].html
β”œβ”€ index-[hash2].html
β”œβ”€ index.js (Contents are `module.exports = './index-[hash1].html'`
β”œβ”€ sub/
β”‚  └─ index.js (Contents are `module.exports = '../index-[hash2].html'`
└─ esm/
   └─ index.js (Contents are the top level index.js, the sub/index.js contents are just gone, probably overridden)

I'm okay with the hashes (as long as it's consistent), but it looks like it's not doing things relative to the outdir per the "lowest common ancestor" rule outlined in the esbuild documentation. And them it seems to be just completely overriding some files.

Admittedly, I'm pretty new to esbuild. I could me misconfiguring things. Would love some direction if I'm missing something.

Extract comments from code

The acorn parser does not extrapolate comments in the code, so we are not able to detect magic imports from the AST. Provide an API to collect comments across plugins.

Environment variables with non alphanumeric characters breaks esbuild

In es-plugin-env, if an environment variable contains symbols, CommonProgramFiles(x86) for example, esbuild throws an error.

error: The define key "process.env.CommonProgramFiles(x86)" contains invalid identifier "CommonProgramFiles(x86)"

Error: Build failed with 2 errors:
error: The define key "process.env.CommonProgramFiles(x86)" contains invalid identifier "CommonProgramFiles(x86)"
error: Cannot read properties of undefined (reading 'errors')
    at failureErrorWithLog (D:\Code\Test\esbuild test\node_modules\esbuild\lib\main.js:1494:15)
    at D:\Code\Test\esbuild test\node_modules\esbuild\lib\main.js:1151:28
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  errors: [
    {
      detail: undefined,
      location: null,
      notes: [],
      pluginName: '',
      text: 'The define key "process.env.CommonProgramFiles(x86)" contains invalid identifier "CommonProgramFiles(x86)"'
    }
]

I believe this is the root of the issue:

export function defineEnvVariables() {
/**
* @type {{ [key: string]: string }}
*/
const definitions = {};
definitions['process.env.NODE_ENV'] = JSON.stringify(process.env.NODE_ENV || 'development');
Object.keys(process.env).forEach((key) => {
if (isNaN(parseFloat(key))) {
definitions[`process.env.${key}`] = JSON.stringify(process.env[key]);
}
});
definitions['process.env'] = '{}';
return definitions;
}

esbuild-plugin-html does not use the original directory of the source file

I'm trying to use esbuild-plugin-html with the esbuild-plugin-glob plugin to use it with several html files, like this:
[ "pages/**/*.html", "dynamic_pages/**/*.html"];

This breaks as the code now just uses the filename and not the directory. I hacked it to work by changing the creation of the finalOutputFile const in the index.js around line 28595 to this:
const finalOutputFile = `${workingDir}/${outDir}/${inputs[0]}`;

I'm not making a PR as I have a hard time understanding the original way of setting the path and what else in the code it affects but maybe you can have a look and adjust it so it does take the path into account?

Could not find MIME for Buffer <null>

await esbuild
    .build({
      entryPoints: [`${outdir}/index.html`],
      outdir: outdir,
      minify: true,
      sourcemap: false,
      bundle: true,
      write: true,
      plugins: [
        htmlPlugin({
          // scriptsTarget: "es6",
          // modulesTarget: "es2020",
        }),
      ],
      assetNames: "[name]",
    })

After running the script, I got follow error:
image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.