Coder Social home page Coder Social logo

googlefeud / ts-macros Goto Github PK

View Code? Open in Web Editor NEW
331.0 331.0 9.0 7.31 MB

A typescript transformer / plugin that allows you to write macros for typescript!

Home Page: https://googlefeud.github.io/ts-macros/

License: MIT License

TypeScript 90.41% JavaScript 8.25% CSS 1.34%
hacktoberfest macros typescript

ts-macros's People

Contributors

googlefeud avatar gurimarukin avatar joehinkle11 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  avatar

ts-macros's Issues

[BUG] ts-macros tries to evaluate a function as a macro when it is not preceded by `$`

Describe the bug
Unfortunately, I have not been able to reduce to a minimal test case.

I have this class:

export class Operation extends Node<OperationValue> {
  async eval(context: Context): Promise<Node> {
    let [left, op, right] = this.value
    if (!left.operate) {
      throw new TypeError(`Cannot operate on ${left.type}`)
    }
    return await this.evalIfNot(context, async () => {
      if (context.shouldOperate(op)) {
        left = await left.eval(context)
        right = await right.eval(context)
        return left.operate!(right, op, context)
      }
      let node = this.clone()
      node.evaluated = true
      return node
    })
  }
}

I am getting the following error when trying to build with TypeScript:

error TS8000: No possible candidates for "operate" call

25         return left.operate!(right, op, context)

What's confusing is that the documentation seems to imply that a macro always requires a preceding $ character, which does not exist here. However, when I tried to create a simple test case in the ts-macros playground, I couldn't reproduce it.

I'm refactoring so I don't need a non-null assertion here, but I thought I should report it anyway.

[BUG] Postfix macros in the playground are marked with errors by the type checker

Describe the bug
When using postfix macros (in playground at least), they do not pass the type checker and get underlined with an error "Property does not exist on type".

Code to reproduce

function $try(resultObj: Save<{ value?: number, is_err: () => boolean}>) {
    $$escape!(() => {
        if (resultObj.is_err()) {
            return resultObj;
        }
    });
    return resultObj.value;
}

const val1 = $try!({ value: 123, is_err: () => false });
const val2 = ({ value: 123, is_err: () => false }).$try!();
//                                                 ~~~~
// Property '$try' does not exist on type '{ value: number; is_err: () => boolean; }'.(2339)
//
// typeof val2 === any.

Expected behavior
Expected val2 to have the same type as val1, and no errors to be displayed.

Imports are not working inside $$raw macro

Hello, I've been dabbling around the lib for a while. I just wanna know how to make imports work inside the $$raw macro.

Here is an example

import { $$raw, RawContext } from "ts-macros";
import * as fs from "fs";

export function $writeMyFile(): string {

  return $$raw!((ctx: RawContext) => {
    const myfile = fs.readFileSync('file.txt', 'utf8')

    return ctx.factory.createStringLiteral(myfile.toString());
  });
}

But this macro does not compile and gives me an error

$fs is not defined
9     var css = fs.readFileSync('file.txt', 'utf8');

I'm aware that $$readFile macro exists, but I wanted to do something beyond that (which includes writing, using third party libs, etc.). I'd also be grateful to have an insight on how does $$raw macro actually work, since it gives me some issues to (just like this one) which are non-existent outside of its scope.

[BUG] Unhandled SyntaxKind: Unknown (regression?)

Hi again.

Describe the bug
When using version 2.2.1 (with either [email protected] or [email protected]), I get that cryptic error when I use a certain macro ($$readFile for instance) in certain scenarios.

Code to reproduce
Apparently, we cannot use $$readFile in a namespace...

// ❌ error.ts
import { $$readFile } from "ts-macros";

namespace Foo {
  $$readFile!("src/index.ts");
}

or in a object literal AND in a class...

// ❌ error.ts
import { $$readFile } from "ts-macros";

const Foo = {
  a: $$readFile!("src/index.ts"),
};

class Bar {
  foo() {
    $$readFile!("src/index.ts");
  }
}

but we can use $$ts inside a namespace...

// ✅ fine.ts
import { $$ts } from "ts-macros";

namespace Foo {
  $$ts!(";");
}

or $$readFile in a object literal...

// ✅ fine.ts
import { $$readFile } from "ts-macros";

const Foo = {
  a: $$readFile!("src/index.ts"),
};

or $$readFile in a class...

// ✅ fine.ts
import { $$readFile } from "ts-macros";

class Bar {
  foo() {
    $$readFile!("src/index.ts");
  }
}

Expected behavior

Additional context
Doesn't occur in 2.1.0. Not sure if this is related to ts-patch or typescript=^5.

[FEATURE] Expose MacroError for consumers

Is your feature request related to a problem? Please describe.
MacroError is a fantastic way of telling the user what the error. I think if someone is writing custom macros, they should be able to throw using MacroError.

Describe the solution you'd like

import {MacroError} from 'ts-macro'

function $justThrow() {
  $$raw((ctx) => {
    throw MacroError(ctx, "This just fails")
  })
}

Describe alternatives you've considered
I have copy pasted all the code from the library to throw an error from my macro.

Additional context
None

[BUG] ESM type imports are not modified by compiler

Describe the bug
ESM imports that contain the type keyword are not modified by the compiler when the transform is enabled.

Code to reproduce

import type { foo } from './test.js';
import { bar, type baz } from './test.js';

Expected behavior
Imports should be modified by the compiler to only include "real" imports.
The above example should compile to the following:

import { bar } from './test.js';

Additional context
Note: This issue also occurred while using ttypescript (@1.5.15) with typescript (@4.9.5).
Version info:

[BUG] Apparent failure of "hygienic-ness" in 2.4.0

Describe the bug
The code below produces the following error on npx tsc:

src/bug240.ts:1:7 - error TS2451: Cannot redeclare block-scoped variable 'temp_1'.

1 const temp_1 = 5;export const a = temp_1;const temp_1 = "five";export const b = temp_1;
        ~~~~~~

src/bug240.ts:1:48 - error TS2451: Cannot redeclare block-scoped variable 'temp_1'.

1 const temp_1 = 5;export const a = temp_1;const temp_1 = "five";export const b = temp_1;
                                                 ~~~~~~

Found 2 errors in the same file, starting at: src/bug240.ts:1

Code to reproduce

import {$$escape} from 'ts-macros'

function $dummy<T>(expr: T) {
   return $$escape!(() => {
      const temp: T = expr;
      return temp;
   })
}

export const a = $dummy!(5);
export const b = $dummy!('five');

Expected behavior
The code should compile and run without error; presumably the object code would use two different identifiers for the temporary variables in each of the two calls to $dummy!

Additional context
pnpm ls produces:

devDependencies:
@types/node 20.5.0
source-map 0.7.4
ts-macros 2.4.0
ts-patch 3.0.2
typescript 5.1.6

tsconfig.json contains:

{
    "compilerOptions": {
        "target": "ES2022",
        "rootDir": "./src",
        "outDir": "./build",
        "moduleResolution": "nodenext",
        "plugins": [ {
            "transform": "ts-macros/dist/type-resolve",
            "transformProgram": true
        } ]
    }
}

[BUG]different results when file is split

Describe the bug
Splitting files generates strange code.

Code to reproduce

input

// src/Vector2.ts
export namespace Vector2 {
    export function $new(): Vector2 {
        return [0, 0] as unknown as Vector2;
    }

    export function $from(x: number, y: number): Vector2 {
        return [x, y] as unknown as Vector2;
    }
}

export interface Vector2 {
    $x(): number;
    $y(): number;
}

function $asData(v: Vector2): [number, number, number] {
    return v as unknown as [number, number, number];
}

export function $x(v: Vector2): number {
    return $asData!(v)[0];
}

export function $y(v: Vector2): number {
    return $asData!(v)[1];
}
// src/index.ts
import { Vector2 } from '../Vector2';

const vec1 = Vector2.$new!();
console.log(vec1.$x!());
console.log(vec1.$y!());

const vec2 = Vector2.$from!(1, 2);

export {}

output

// src/Vector2.js
export var Vector2;
(function (Vector2) {
})(Vector2 || (Vector2 = {}));
// src/index.js
import { Vector2 } from '../Vector2';
const vec1 = [v, c1];
console.log(vec1[]);
console.log(vec1[]);
const vec2 = [Vector2, 1];

Expected behavior

// src/index.js
import { Vector2 } from '../Vector2';
const vec1 = [0, 0];
console.log(vec1[0]);
console.log(vec1[1]);
const vec2 = [1, 2];

Additional context
If i don't split the file, the Expected Behavior will come out properly.

I'm sorry for making an issue again even though it hasn't been long since you patched :[

How to escape $$inlineFunc macro?

Hello!

I try to write something like this:
https://googlefeud.github.io/ts-macros/playground/?code=FAehAIEkDsBcFMBOBDAxrAlgN3uADgDbICeA5ogPYCu0AJgISgQDqiGC4x1i4qFtuABZJcyOuADO8XLGHhYKaBLwYC8WuETwJVArAB0TcAEECBecTzbwYjQDMa6DBSXg7lALby5BDACMURGIbLRsCLWRaYIwPPApEBAZgYAdoJxdwABIPZDwAHgAVABpwACUAPgAKZEREAC5wAGVkHDzjWpJC8vKS1D8GyvZ4DwaCgEpwAF5ysomG9pRiPIrwAG9gcE2szO1UXPh6SsqJ6bWNrYu+JVhNa0nwAG0AXQBuc4vNu3jwSrUbjEmAAYXuAMOA8iFEPo1NBSLIQRgANSIibrD4fK4SG54KgSYQae6ZHYSPZWQ7HKYzIkYaC+aDwABijkOfRKNUQDwwTzGYze6IuWgk+hxeMqIvxvPeHwAvlLpZKBfBYFRENBthgBHBDgAiQXayWy4BHE4zNFbbK5Q4PACMJQATCUAMwlAAsJQArCUAGwlADsJQAHCUAJxPEqVaBUDwmta3ZWq8CRrwAKnAdpB8pBYHAHl0mHAdNwqVQwHlxzeQA

But emitted result not expected:

(() => {
    return arr_1[i_1] * 2;
})();
const pushed_1 = null;

Want to see something like this:

const pushed = arr_1[i_1] * 2;
const pushed_1 = pushed;

I know, that $$inlineFunc can be passed directly to variable:
https://googlefeud.github.io/ts-macros/playground/?code=FAehAIEkDsBcFMBOBDAxrAlgN3uADgDbICeA5ogPYCu0AJgISgQDqiGC4x1i4qFtuABZJcyOuADO8XLGHhYKaBLwYC8WuETwJVArAB0TcAEECBecTzbwYjQDMa6DBSXg7lALby5BDACMURGIbLRsCLWRaYIwPPApEBAZgYAdoJxdwABIPZDwAHgAVABpwACUAPgAKZEREAC5wAGVkHDzjWpJC8vKS1D8GyvZ4DwaCgEpwAF5ysomG9pRiPIrwAG9gcE2szO1UXPh6SsqJ6bWNrYu+JVhNa0nwAG0AXQBuc4vNu3jwSrUbjEmAAYXuAMOA8iFEPo1NBSLIQRgANSIibrD4fK4SG54KgSYQae6ZTIYaC+aDwABijkOfRKNUQDwwTzGb3RFy0En0OLxlW5+JZ7w+AF9BUKBez4LAqIhoNsMAI4IcAEQcpUCkXAI4nGZorbZXKHB4ARhKACYSgBmEoAFhKAFYSgA2EoAdhKAA4SgBOJ4lSrQKgebVrW5SmXgANeABU4FNILFILA4A8ukw4DJuFSqGAYuObyAA

But self-executing function has additional runtime cost and desire to avoid it totally:

const pushed_1 = (() => {
    return arr_1[i_1] * 2;
})();
res_1.push(pushed_1);

[BUG] Module augmentation causes import to be tree-shaken (is removed from output)

I'm not certain this is a ts-macros bug or a ts-patch bug, but my perception would be that:

  1. ts-patch shouldn't be doing anything except allowing patching (unless that's incorrect)
  2. ts-macros is the only transformer I'm using

The bug and all its details are here.

To re-summarize:

// chevrotain.d.ts
declare module 'chevrotain' {
  interface CstParser {
    // things that augment this type
  }
}


// cssParser.ts
import { CstParser, EOF } from 'chevrotain'

class CssParser extends CstParser {
 // class implementation
}

// outputs
import { EOF } from 'chevrotain'

Someone on the TypeScript team investigated and said that disabling ts-patch (which disables ts-macros) solves this bug.

[BUG] macro not work properly after 1.3.4

Describe the bug
A clear and concise description of what the bug is.

When you split a file, the macro behaves strangely

Code to reproduce
Paste the relevant code which reproduces the error, or give detailed instructions on how to reproduce it.

input:

// test.ts
export function $m1() {
    return 1;
}
// index.ts
import { $m1 } from "./test";

const m1 = $m1!();
m1;

output:

// index.js
import { $m1 } from "../test";
const m1 = null;
m1;

Expected behavior

// index.js
import { $m1 } from "../test";
const m1 = 1;
m1;

Additional context
Add any other context about the problem here.

It should also be possible to duplicate the names of macros on a file-by-file basis

For example, if file1.ts has a macro named m1 and file2.ts has a macro named m1, you should be able to use the macro you want, depending on which one you import.

// file1.ts
export function $m1() {
    return 1;
}
// file2.ts
export function $m1() {
    return 2;
}
// index.ts
import { $m1 } from './file1';
import { $m1 as $m2 } from './file2';

const test = $m1!(); // it should be 1
const test = $m2!(); // it should be 2

[BUG] Recursive methods cause stack overflow exception with the `$$raw` macro

Describe the bug
When a recursive method is defined inside of $$raw then I always end up getting a stack overflow exception.

Code to reproduce

import { $$raw } from "ts-macros"

export function $recurse() {
  return $$raw!((ctx, arg) => {
    function loop(i: number): number {
      return i > 10 ? i : loop(i + 1)
    }

    return ctx.factory.createStringLiteral(loop(0).toString())
  })
}

const foo = $recurse!()

Expected behavior
There shouldn't be any stackoverflow

Additional context
Nothing

Question: Is it possible to do a "type-safe" repetition?

Is your feature request related to a problem? Please describe.
Suppose I make a macro:

function $reflect<T>(expr: T) {
   expr.reflectedType = $$typeToString!<T>(true, false, true);
}

Then indeed the following code

export const foo = {x: 1, y: 2}
export const bar = {a: 'A', b: 'B'}
export const baz = {yes: true, no: false}
$reflect!(foo)
$reflect!(bar)
$reflect!(baz)

generates

export const foo = {x: 1, y: 2}
export const bar = {a: 'A', b: 'B'}
export const baz = {yes: true, no: false}
foo.reflectedType = "{x: number, y: number}";
bar.reflectedType = "{a: string, b.string}";
baz.reflectedType = "{yes: boolean, no: boolean}";

as desired (ignore for the moment that it then generates a TypeScript error since the type of foo has no reflectedType property).

But what If I would like to create a different macro so that I could abbreviate the last three lines with say:

$reflectTypes!([foo, bar, baz])

and generate the exact same code?

Describe the solution you'd like
I'd like there to be a way to cycle through an array of literals, accessing the type of each in turn.

Describe alternatives you've considered
I tried

export function $reflectTypes<ImplTuple>(tup: ImplTuple) {
   +[[tup], <T>(elt: T) =>
      elt.reflectedType = $$typeToString!<T>(true, false, true)]
}

but that fails with

TS8000: `typeToString` macro expects one type parameter.

even though it certainly looks like I gave it one type parameter. I also tried the more baroque

export function $reflectTypes<ImplTuple>(tup: ImplTuple) {
   +[[tup, [0,1,2]], <N extends number>(elt: ImplTuple[N], n: N) =>
      elt.reflectedType = $$typeToString!<ImplTuple[N]>(true, false, true)]
}

and that actually seems to get through macro processing but it produces "{}" for all of the type strings.
Maybe I am just missing how this should be done? If so, guidance would be welcome. Thanks!

Additional context
Currently running ts-macros 2.5.0, ts-patch 3.0.2

Request: example of ts-macros working with Vite

I'd like to use ts-macros with Vite, a successor to create-react-app and a popular modern development framework for web.

May it be possible for you to create an example repository of ts-macros correctly configured with Vite? If so, it would be great if the example repository could also include a small custom macro.

Thanks, I'd really appreciate it!

[BUG]complie error when use overloading

Describe the bug
Overloading macros will not allow compilation.

src/test/index.ts:1:47 - error TS8000: Macro $Vector3 is already defined.

1 function $Vector3(): [number, number, number];
 
2
 
3 function $Vector3(x: number, y: number, z: number): [number, number, number];
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Code to reproduce

function $Vector3(): [number, number, number];

function $Vector3(x: number, y: number, z: number): [number, number, number];

function $Vector3(x = 0, y = 0, z = 0): [number, number, number] {
    return [x, y, z];
}

const vec1 = $Vector3!(1, 2, 3);

Expected behavior
Compilation should be done well as below

const vec1 = [1, 2, 3];

Additional context
If the macro is not intended for overloading, it would be better to make other errors

Access to type-parameters

Hi!
Thanks for this amazing library! This should be officially supported by typescript IMHO 🙏

I am trying to create schema for types using the $$raw macro. However, I am unable to figure out how to get access to the type-param that's being passed to the macro for eg:

Input

type Foo = {
  bar: string
}

const FooSchema = $generateSchema!<Foo>()

The expected output should look like something like below.
Output

type Foo = {
  bar: string
}

const FooSchema = {
  name: Foo,
  fields: [
    {name: "bar", type: "string"}
  ]
}

Since Foo is a type and not a value, how do I get access to it in my macro?

[BUG] Macro expansion is not updated when running under webpack --watch

Describe the bug
Macro and the invoking need to be in separate file

// def.ts

export function $macro(a: number, b: number) {
return a + b;
}
// usage.ts
import {$macro} from "./def.ts"

console.log($macro!(5, 4));

Code to reproduce
Load the files with ts-loader.

webpack --watch
#all good
# edit macro def
# still old result

Expected behavior
Updated result

Additional context
I'm not sure there's anything you can do here, this seems to be related to ts-loader.

[BUG] Imports of macros should be removed (ESM modules issue)

Describe the bug
Currently, imports of macros are kept, but they should be eliminated.
Other than being a design expectation/requirement (there shouldn't be macros leftovers in the executable code), it also causes a runtime exception.

Code to reproduce

The following code

// macros.ts

// @internal
export function $test() {
    console.log("Test!");
}
// index.ts
import { $test } from "./macros.js";

$test!();

gets transpiled to:

// macros.js

export {};
// index.js
import { $test } from "./macros.js"; // <--- this should have been removed

console.log("Test!");

This isn't a problem in CommonJs, but it is in ECMAScript modules (The requested module './macros.js' does not provide an export named '$test').

Expected behavior
Imports of macros should be removed!

Additional context
NA

[BUG] Compilation error when using real decorators

Describe the bug
When using a decorator either on fields, methods, accessors or classes, the compilation fails with TypeError: Cannot read properties of undefined (reading 'kind') here.
Of course, this only happens when using ts-macros (with ttypescript).

Code to reproduce
The following snippet is enough to reproduce the bug on the playground (you don't have to use ts-macro at all):

declare function decorator(...args: any[]): any

class Foo {
    @decorator // <--- comment out to make it work
    get bar() {
        return 0;
    }
}

Expected behavior
It should compile!

Additional context
I believe this is ts-macro specific-issue - I already use ttypescript with another transformer in a repo of mine and it works fine.

[BUG]Generate wrong code when using namespace

Describe the bug
Generates invalid code for macros nested in namespace.

Code to reproduce

input

namespace Vector3 {
    export function $from(x: number, y: number, z: number): [number, number, number] {
        return [x, y, z];
    }
}

const vec1 = Vector3.$from!(1, 2, 3);

output

var Vector3;
(function (Vector3) {
})(Vector3 || (Vector3 = {}));
const vec1 = [Vector3, 1, 2];

Expected behavior

var Vector3;
(function (Vector3) {
})(Vector3 || (Vector3 = {}));
const vec1 = [1, 2, 3];

[BUG] Why does parameter order affect type resolution?

Describe the bug
The order of the parameters to a generic macro appears to affect the type it believes it is instantiated with.

Code to reproduce

function $tplFirst<T>(expr: T, name: string) {
   console.log('Deduced', name, '=', expr, 'is a', $$typeToString!<T>())
}

function $tplSecond<T>(name: string, expr: T) {
   console.log('Believe', name, '=', expr, 'is a', $$typeToString!<T>())
}

$tplFirst!([37, 45], 'correctly')
$tplSecond!('bizarrely', [37, 45])

Expected behavior
Output:

Deduced correctly = [37, 45] is a number[]
Believe bizarrely = [37, 45] is a number[]

Actual behavior
Output:

Deduced correctly = [ 37, 45 ] is a number[]
Believe bizarrely = [ 37, 45 ] is a string

** Additional context **
I'm trying to make a macro that represents a named, type-annotated expression, so it's very natural that the name should come first in the macro call. I'd really like to find a way to deduce the type of the expression (and get information about that type) while allowing the users of the macro to put the name first.

[BUG] Not working properly in rollup

Describe the bug
The macro does not generate code when dividing files in rollup.

Code to reproduce

sources:

//macro.ts
export function $test() {
    console.log("test");
}
//index.ts
import { $test } from "macro";

function $test2() {
    console.log("test2");
}

class Test {
    constructor() {
        $test!();
        $test2!();
    }
}

export default Test;

setting fIles:

//package.json
{
  "name": "macro-rollup",
  "version": "1.0.0",
  "scripts": {
    "build": "rollup -c"
  },
  "devDependencies": {
    "rollup": "^2.79.1",
    "rollup-plugin-ts": "^3.0.2",
    "ts-macros": "^1.3.6",
    "typescript": "^4.8.4"
  }
}
//rollup.config.js
import rollupPluginTs from "rollup-plugin-ts";
import tsMacroTransformer from 'ts-macros';
import tsConfig from "./tsconfig.json";

export default {
    input: 'index.ts',
    output: {
        file: 'dist/index.js',
        format: 'esm'
    },
    plugins: [
        rollupPluginTs({
            tsconfig: {
                ...tsConfig.compilerOptions,
            },
            transformers: (options) => ({
                before: [tsMacroTransformer(options.program)]
            })
        })
    ]
}
//tsconfig.json
{
    "compilerOptions": {
        "target": "ES6",
        "outDir": "dist",
        "strict": true,
        "baseUrl": "./",
        "esModuleInterop": true,
        "downlevelIteration": true,
        "moduleResolution": "Node",
        "forceConsistentCasingInFileNames": true,
        "stripInternal": true,
        "resolveJsonModule": true
    }
}

rollup build result:

//index.js
class Test {
    constructor() {
        console.log("test2");
    }
}

export { Test as default };

Expected behavior

//index.js
class Test {
    constructor() {
        console.log("test");
        console.log("test2");
    }
}

export { Test as default };

Additional context

I think this is a fatal 🤔

[BUG] `Save<...>` changes result type

Describe the bug
When adding Save<> around an argument type, return type changes for some reason.

Code to reproduce

abstract class _Result<T, E> {
  public value: T | E;
}
class Ok<T> extends _Result<T, never> {
  constructor(public value: T) {
    super();
  }
}
class Err<E> extends _Result<never, E> {
  constructor(public value: E) {
    super();
  }
}
type Result<T, E> = Ok<T> | Err<E>;

function $try<T, E>(result: Save<Result<T, E>>): T {
  $$escape!(() => {
    if (result instanceof Err) {
      return result;
    }
  });
  return result.value as T;
}

declare const result: Result<null, string>;
const z = $try!(result);
//    ^? z: string

Expected behavior
Expected type of z to be null.

Additional context
After removing Save from type of $try.result, type of z becomes null, as expected.

[BUG] error when build twice

Describe the bug
If you build more than once using gulp, there will be an error.

Code to reproduce

// package.json
{
  "name": "ts-macros-bug",
  "version": "1.0.0",
  "scripts": {
    "build": "gulp"
  },
  "devDependencies": {
    "gulp": "^4.0.2",
    "gulp-typescript": "^6.0.0-alpha.1",
    "ts-macros": "^1.3.3",
    "typescript": "^4.8.3"
  }
}
// index.ts
function $someMacro() {
    console.log('Hello world!');
}

$someMacro!();
// gulpfile.js
const gulp = require('gulp');
const ts = require('gulp-typescript');
const macroTransformer = require('ts-macros').default;

gulp.task('build1', function () {
    return gulp.src('index.ts')
        .pipe(ts({
            outFile: 'output1.js',
            getCustomTransformers: (program) => ({
                before: [ macroTransformer(program) ]
            })
        }))
        .pipe(gulp.dest('.'));
});


gulp.task('build2', function () {
    return gulp.src('index.ts')
        .pipe(ts({
            outFile: 'output2.js',
            getCustomTransformers: (program) => ({
                before: [ macroTransformer(program) ]
            })
        }))
        .pipe(gulp.dest('.'));
});

gulp.task('default', gulp.series('build1', 'build2'));

If you build this project through npm run build, you will get an error.

[03:11:54] Using gulpfile ~\Desktop\ts-macros-bug\gulpfile.js
[03:11:54] Starting 'default'...
[03:11:54] Starting 'build1'...
[03:11:56] Finished 'build1' after 1.92 s
[03:11:56] Starting 'build2'...
index.ts:1:1 - error TS8000: Macro $someMacro is already defined.

1 function $someMacro() {
  ~~~~~~~~~~~~~~~~~~~~~~~
2     console.log('Hello world!');
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 }
  ~

Expected behavior
error should not appear.

Additional context
I think this is a problem that happens because the transformer has a global variable that stores macros.

[BUG] Calling static method on a class from identifier crashing playground

[BUG] Unable to import macros from files

I'm trying to create a logging macro for Typescript that I want to use across my project.

Using a macro inside the same file works:

// replication.ts

function $logInfo(domain: string | object, message: string) {
	console.log(`(${domain}) ${message}`);
}

class Replication {
	static toString() {
		return 'Replication';
	}

	static connect() {
		$logInfo!(this, 'connect');
	}
}

// index.bundle.js

class Replication {
    static toString() {
        return 'Replication';
    }
    static connect() {
        console.log(`(${this}) ${"connect"}`);
    }
}

I bundle my Typescript with Webpack using the ts-loader.

But if try to import my macro from another file, my macro is omitted entirely from the bundled output:

// logging.ts

function $logInfo(domain: string | object, message: string) {
	console.log(`(${domain}) ${message}`);
}

export {
	$logInfo,
};

// replication.ts

import {
	$logInfo 
} from './logging';

class Replication {
	static toString() {
		return 'Replication';
	}

	static connect() {
		$logInfo!(this, 'connect');
	}
}

// index.bundle.js

class Replication {
    static toString() {
        return 'Replication';
    }
    static connect() {
    }
}

I would like to know what the best practices are when it comes to sharing macros between files.

Here's the relevant portion of my Webpack configuration:

module: {
	rules: [
		// compile typescript

		{
			test: /\.ts$/,
			exclude: /node_modules/,
			use: [
				{
					loader: 'ts-loader',
					options: {
						configFile: 'tsconfig.json',
						compilerOptions: {
							soureMap: Boolean(devtool),
							outDir: path.resolve(targetPath, 'js')
						},
						getCustomTransformers: (program) => ({
							before: [TsMacros.default(program)]
						}),
					},
				},
			],
		},
	],
],

[BUG]: Macro expansion fails when defined inside objects

Describe the bug
If there is a method that is defined on an object literal which uses $$raw there the expansion fails.

Code to reproduce

const Foo = {
    $bar() {
         $$raw!((ctx) => {
            return ctx.factory.createStringLiteral("Yello!")
        })
    }
}

Foo.$bar!()

Expected behavior
Macro expansion to succeed. Instead one gets the following error

Error: `raw` macro must be called inside another macro.

Additional context
None

[BUG] Imports are wrongly removed

Describe the bug
Some imports are removed by accident, even if there is no macro defined or invoked.

Code to reproduce

//index.ts
import * as mod from "./module";
mod.fn2();
//module.ts
export default function fn() {
        console.log("Hello from default");
};
export function fn2() {
        console.log("Hello from fn2");
};

Expected behavior
After transpilation, "./module" import should be kept.
Additional context
Reproduces with ttypescript & ts 4.9 as well as with ts-loader and ts 5.0

[FEATURE] better usage example in readme

You have a screen capture of a live transformation in the readme, that could be perfectly vanilla javascript with:

console.log(["a", "b"].includes(searchItem))

as someone knowing their languages, I usually advice against purely esoteric usage examples like this.

could someone come up with a better example for the readme?

[BUG] Produces redundant code

Describe the bug
Emitted code reuses the incoming value multiple times.

function $try(result: Result<unknown, unknown>) {
    if (result.is_err()) {
        return result;
    }

    return result.value;
}

console.log(
    $try!(Err(new Error('foo'))),
);

produces the following output:

console.log((() => {
    if ((0, result_1.Err)(new Error('foo')).is_err()) {
        return (0, result_1.Err)(new Error('foo'));
    }
    return (0, result_1.Err)(new Error('foo')).value;
})());

Expected behavior

Should not emit redundant code.

Additional context
I want to have a macro that allows for something like try!() or Rusts' result?.

[BUG]macros chaining problem

Describe the bug
A clear and concise description of what the bug is.

macros chaning not works properly

Code to reproduce
Paste the relevant code which reproduces the error, or give detailed instructions on how to reproduce it.

input:

// Vector2.ts
export namespace Vector2 {
    export function $new(): Vector2 {
        return [0, 0] as unknown as Vector2;
    }
}

export interface Vector2 {
    $x(): number;
    $y(): number;
}

function $asData(v: Vector2): [number, number] {
    return v as unknown as [number, number];
}

export function $x(v: Vector2): number {
    return $asData!(v)[0];
}

export function $y(v: Vector2): number {
    return $asData!(v)[1];
}
//index.ts
import { Vector2 } from "./Vector2";

const vec1 = Vector2.$new!();
console.log(vec1.$x!());
console.log(vec1.$y!());

output:

import { Vector2 } from "./Vector2";
const vec1 = [0, 0];
console.log(null);
console.log(null);

Expected behavior
A clear and concise description of what you expected to happen.

output should be:

import { Vector2 } from "./Vector2";
const vec1 = [0, 0];
console.log(vec1[0]);
console.log(vec1[1]);

Additional context
Add any other context about the problem here.

In version 1.3.3, you saved the name, not the symbol of the macro,
so it would have been possible to decide which macro to map to when chaining.

However, by storing symbols, it became impossible to know which macro to map to when chaining.
So to solve this problem, I think we need a syntax that determines which macro to actually map to when declaring a macro member on an interface.

for example:

//new builtin function
type Keys<T> = {
    [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

type MacroMap<T> = {
    [K in Keys<T>]: (v: T) => ReturnType<T[K]>;
};

export function $$macroMapper<T, M extends MacroMap<T> = MacroMap<T>>(map: M): void {
    map;
}
export namespace Vector2 {
    export function $new(): Vector2 {
        return [0, 0] as unknown as Vector2;
    }
}

export interface Vector2 {
    $x(): number;
    $y(): number;
}

function $asData(v: Vector2): [number, number] {
    return v as unknown as [number, number];
}

export function $x(v: Vector2): number {
    return $asData!(v)[0];
}

export function $y(v: Vector2): number {
    return $asData!(v)[1];
}

$$macroMapper<Vector2>({
    $x: $x,
    $y: $y
}); // now we know what macro to map.

[BUG] More than one possible candidate for "$x" call

Describe the bug
I think this code has enough information(this) to specify the method. Why not?

Code to reproduce

code:

export namespace Vector2 {
    export namespace Methods {
        export function $asData(v: Vector2): [number, number] {
            return v as unknown as [number, number];
        }

        export function $x(self: Vector2): number {
            return $asData(self)[0];
        }

        export function $y(self: Vector2): number {
            return $asData(self)[1];
        }
    }
    
    export function $from(x: number, y: number): Vector2 {
        return [x, y] as unknown as Vector2;
    }
}

interface Vector2 {
    $x(): number;
    $y(): number;
}

export namespace Vector3 {
    export namespace Methods {
        export function $asData(v: Vector3): [number, number, number] {
            return v as unknown as [number, number, number];
        }

        export function $x(self: Vector3): number {
            return $asData(self)[0];
        }

        export function $y(self: Vector3): number {
            return $asData(self)[1];
        }
    }

    export function $from(x: number, y: number, z: number): Vector3 {
        return [x, y, z] as unknown as Vector3;
    }
}

interface Vector3 {
    $x(): number;
    $y(): number;
    $z(): number;
}

const v2 = Vector2.$from!(1, 2);
console.log(v2.$x!(), v2.$y!());

error:

src/index.ts:53:13 - error TS8000: More than one possible candidate for "$x" call

53 console.log(v2.$x!(), v2.$y!());

Expected behavior
A clear and concise description of what you expected to happen.

const v2 = [1, 2];
console.log(v2[0], v2[1]);

Additional context
Add any other context about the problem here.

[FEATURE] something like $$cond macro

Is your feature request related to a problem? Please describe.
I want to extract some boolean on compile time via TS AST and emit one or another code.

Describe the solution you'd like

function $testMacro(param?: number) {
  return $$cond!(
    (ctx, paramAst) => !!paramAst, // params similar $$raw; check that param passed
    () => $$escape(() => {
      // code for passed number `param`
    }),
    () => $$escape(() => {
      // code for undefined `param`
    }),
  )
}

Describe alternatives you've considered
I don't understand how to implement example above ☝️ using existing instruments now (v2.0.1). Please, provide workaround how to implement conditional macros

[BUG] Does not actually process macros

Describe the bug
When I build the project macros are not processed. There is no form of troubleshooting available and cannot locate any resources on how to fix this.

The instructions provided have not worked for me

Code to reproduce
Code I have works on the playground and not in my project. Code provided in the documentation does not work for me.

Expected behavior
That the macros would actually be processed

Additional context
I have ttypescript added as a dev dependency along with ts-macros
I have added the plugin to tsconfig.json although it says ts-docs and then writes ts-macros but neither work (also the intentation is weird with plugins on the same level as compilerOptions and yet plugins is a child)
I tried out my code in the playground beforehand and even copied over some of the examples and they don't work either.

[BUG] extending interfaces with macros doesn't work with Angular

I was able to integrate ts-macros to Angular14 application using ngx-build-plus library.

Describe the bug
My $contains macro works when calling it like

const needle = 'three';
const haystack = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];

this.needle_found = $contains!(this.needle, ...this.haystack );

the macro itself

export function $contains<T>(value: T, ...possible: Array<T>): boolean {
  return possible.includes(value);
}

But the project does not compile when trying to extend String interface with it.
The error message:
error TS8000: No possible candidates for "$contains" call

declare global {
  interface String {
    $contains<T>(...possible: Array<T>) : boolean;
  }
}

this.needle_found = this.needle.$contains!(...this.haystack);

Code to reproduce
Here is a quick bare bones project to reproduce the error
https://github.com/AlariOis/angular-ts-macros

I know that messing around with private Angular build system API is a good way to shoot yourself in the foot and there is a good chance the problem is not with ts-macros.
Just to clarify - both examples work fine on my node backend, it is the Angular project that has the issue.

[BUG] bugs with conditional macros

[BUG] Maximum call stack size exceeded

Describe the bug
I'm using XRegExp to build complex / nested regular expressions. I'm investigating using ts-macros for this purpose.

Code to reproduce

import * as XRegExp from 'xregexp'

const $buildFragments = (rawFragments: string[][]) => {
  const fragments: Record<string, RegExp> = {}
  rawFragments.forEach(fragment => {
    fragments[fragment[0]] = XRegExp.build(fragment[1], fragments)
  })
  return fragments
}

const fragments: Record<string, RegExp> = $buildFragments!(rawFragments)

(Fragments are regex strings like:)

const fragments = () => [
  ['newline', '\\n|\\r\\n?|\\f'],
  ['whitespace', '[ ]|\\t|{{newline}}'],
  ['ws', '{{whitespace}}+'],
  ['comment', '\\/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*\\/'],
  ['hex', '[\\da-fA-F]'],
  ['unicode', '\\\\{{hex}}{1,6}{{whitespace}}?']
]

Expected behavior
I expected a static object to be output with a map of regular expressions.

Additional context
Add any other context about the problem here.

[BUG] "typeof" is lost after transform

Describe the bug
"typeof" is lost after transform

Code to reproduce

function $what(unit: any) {
    return typeof unit
}

const b = {}
const typeofSomething = $what!(b)

got transformed into

const b = {};
const typeofSomething = b;

Expected behavior
should transform into

const b = {}
const typeofSomething = typeof b;

Additional context
Add any other context about the problem here.

[FEATURE] Tagged template macros?

Is this possible? I think it would be really cool to support tagged templates as macros, helps a lot on the readability.

I wanted to implement something similar to twin.macro

$tw!`flex flex-col justify-center items-center`

any thoughts on this?

[BUG] Warning: Accessing non-existent property 'tracing' of module exports inside circular dependency

Describe the bug
Node.js Warning about circular dependency. View the trace below —

(node:62719) Warning: Accessing non-existent property 'tracing' of module exports inside circular dependency
    at emitCircularRequireWarning (node:internal/modules/cjs/loader:748:11)
    at Object.get (node:internal/modules/cjs/loader:764:5)
    at checkVariableDeclaration (/Users/tushar/Documents/Projects/ts-macro-schema/node_modules/typescript/lib/typescript.js:83763:39)
    at checkSourceElementWorker (/Users/tushar/Documents/Projects/ts-macro-schema/node_modules/typescript/lib/typescript.js:86977:28)
    at checkSourceElement (/Users/tushar/Documents/Projects/ts-macro-schema/node_modules/typescript/lib/typescript.js:86808:17)
    at Object.forEach (/Users/tushar/Documents/Projects/ts-macro-schema/node_modules/typescript/lib/typescript.js:377:30)
    at checkVariableStatement (/Users/tushar/Documents/Projects/ts-macro-schema/node_modules/typescript/lib/typescript.js:83776:16)
    at checkSourceElementWorker (/Users/tushar/Documents/Projects/ts-macro-schema/node_modules/typescript/lib/typescript.js:86946:28)
    at checkSourceElement (/Users/tushar/Documents/Projects/ts-macro-schema/node_modules/typescript/lib/typescript.js:86808:17)
    at Object.forEach (/Users/tushar/Documents/Projects/ts-macro-schema/node_modules/typescript/lib/typescript.js:377:30)

Code to reproduce
Simply run npx ttsc --watch

Expected behavior
No warning should occur

Additional context
package.json

{
  "devDependencies": {
    "jest": "^29.3.1",
    "ts-macros": "^2.0.0",
    "ttypescript": "^1.5.13",
    "typescript": "4.8.4"
  },
  "scripts": {
    "test": "jest",
    "build": "ttsc -b"
  },
  "license": "MIT"
}

[BUG] I'm trying to replicatee something closer to the PROPERTY macro in Qt

Describe the bug
Does not work inside classes

Code to reproduce

function $property() {
    property = 0
}

$property!()

export class ArduPilotSub extends Vehicle.Abstract {
    $property!()

    constructor() {
        super(Vehicle.Firmware.ArduPilot, Vehicle.Type.Sub)
    }
}

link

Expected behavior
The output would be something like:

export class ArduPilotSub extends Vehicle.Abstract {
    property = 0;
    constructor() {
        super(Vehicle.Firmware.ArduPilot, Vehicle.Type.Sub);
    }
}

Ref: https://doc.qt.io/qt-5/properties.html

Additional context
It would be nice to have more examples, like:
How do I use a type as a parameter.
$macro!(number, test, 0)
Generating something like:
const test: number = 0

[BUG] $$typeToString ellipsizes type information

Describe the bug
For complex types, $$typeToString replaces parts of the type description with ...

Code to reproduce
These are the same examples as in #73. I am using the following macro to reflect the types of arrow functions that I am exporting:

export function $implement<Impl>(name: string, expr: Impl) {
   $$define!(name, expr, false, true); // Final `true` is export
   $$ident!(name).reflectedType = $$typeToString!<Impl>();
}

It works fine on

$implement!('square',
   <T>(dep: Dependencies<'multiply', T>) => (z:T) => dep.multiply(z, z))

where square.reflectedType ends up getting the very reasonable value <T>(dep: { multiply: (a: T, b: T) => T; }) => (z: T) => T. However, on

$implement!('sqrt',
   <T>(dep: Dependencies<'equal' | 'conservativeSqrt' | 'unaryMinus', RealType<T>>
       & Dependencies<'zero' | 'complex', T>
       & Dependencies<'absquare' | 're' | 'divideReal', Complex<T>>
       & {
         addTR: Signature<'addReal', T>, 
         addRR: Signature<'add', RealType<T>>,
         addCR: Signature<'addReal', Complex<T>>
      }):
      Signature<'sqrt', Complex<T>> =>
   z => {
      const myabs = dep.conservativeSqrt(dep.absquare(z))
      const r = dep.re(z)
      const negr = dep.unaryMinus(r)
      if (dep.equal(myabs, negr)) {
         // pure imaginary square root; z.im already zero
         return dep.complex(
            dep.zero(z.re), dep.addTR(z.im, dep.conservativeSqrt(negr)))
      }
      const num = dep.addCR(z, myabs)
      const denomsq = dep.addRR(dep.addRR(myabs, myabs), dep.addRR(r, r))
      const denom = dep.conservativeSqrt(denomsq)
      return dep.divideReal(num, denom)
   })

it produces sqrt.reflectedType equal to

<T>(dep: { unaryMinus: (a: RealType<T>) => RealType<T>; conservativeSqrt: (a: RealType<T>) => RealType<T>; equal: (a: RealType<T>, b: RealType<...>) => boolean; } & { ...; } & { ...; } & { ...; }) => (a: Complex<...>) => Complex<...>

so we have lost much of the type information to those ellipses.

Expected behavior
I would like there to be a way to force $$typeToString to produce the entire description of the typescript type of its template argument, without any ellipsization.

Additional context
Typescript 5.1.6, ts-macros 2.4.1, ts-patch 3.0.2

[BUG] Inconsistent variable name mangling when exporting an arrow function definition

Describe the bug
I am using the following macro to reflect the types of arrow functions that I am exporting:

export function $implement<Impl>(name: string, expr: Impl) {
   $$define!(name, expr, false, true); // Final `true` is export
   $$ident!(name).reflectedType = $$typeToString!<Impl>();
}

It works with simple function bodies, but with a more complicated function body, the variables are renamed inconsistently.

Code to reproduce
The following call to the $implement macro works fine:

$implement!('square',
   <T>(dep: Dependencies<'multiply', T>) => (z:T) => dep.multiply(z, z))

(note that Dependencies is just a generic type that looks up the call signature of the 'multiply' function that square will
use).

But it fails on the following case (sorry this is not a very clean example, let me know if you need me to try to pare this down more to get a simpler one that fails):

$implement!('sqrt',
   <T>(dep: Dependencies<'equal' | 'conservativeSqrt' | 'unaryMinus', RealType<T>>
       & Dependencies<'zero' | 'complex', T>
       & Dependencies<'absquare' | 're' | 'divideReal', Complex<T>>
       & {
         addTR: Signature<'addReal', T>, 
         addRR: Signature<'add', RealType<T>>,
         addCR: Signature<'addReal', Complex<T>>
      }):
      Signature<'sqrt', Complex<T>> =>
   z => {
      const myabs = dep.conservativeSqrt(dep.absquare(z))
      const r = dep.re(z)
      const negr = dep.unaryMinus(r)
      if (dep.equal(myabs, negr)) {
         // pure imaginary square root; z.im already zero
         return dep.complex(
            dep.zero(z.re), dep.addTR(z.im, dep.conservativeSqrt(negr)))
      }
      const num = dep.addCR(z, myabs)
      const denomsq = dep.addRR(dep.addRR(myabs, myabs), dep.addRR(r, r))
      const denom = dep.conservativeSqrt(denomsq)
      return dep.divideReal(num, denom)
   })

(this is an algorithm for finding the square root of a complex number, given various other options on complex numbers and on their real parts).

Expected behavior

I would expect both calls to $implement to produce working functions. The first one is fine, but the second one generates

export const sqrt = (dep) => z => {
    const myabs_1 = dep.conservativeSqrt(dep.absquare(z));
    const r_1 = dep.re(z);
    const negr_1 = dep.unaryMinus(r);
    if (dep.equal(myabs_1, negr_1)) {
        // pure imaginary square root; z.im already zero
        return dep.complex(dep.zero(z.re), dep.addTR(z.im, dep.conservativeSqrt(negr_1)));
    }
    const num_1 = dep.addCR(z, myabs);
    const denomsq_1 = dep.addRR(dep.addRR(myabs, myabs), dep.addRR(r, r));
    const denom_1 = dep.conservativeSqrt(denomsq);
    return dep.divideReal(num_1, denom_1);
};

which fails to execute because some of the occurrences of myabs (for example) have been renamed to myabs_1, but others have not, so the generated code is no longer self-consistent.

Additional context
Typescript 5.1.6, ts-macros 2.4.1, ts-patch 3.0.2

I don't see why ts-macros needs to rename variables at all inside the function body z => { CODE... }, since that CODE is in its own scope that will not bleed variable names into it or out of it, so far as I can see...

[FEATURE] incremental compilation support

Is your feature request related to a problem? Please describe.
There was a problem implementing hot reloading using gulp-typescript.

https://github.com/ivogabe/gulp-typescript#incremental-compilation
when performing an hot reloading in the way that you see in this document,
the entire project is recompiled when hot reloading, which takes a very long time.

The gulp-typescript recompiles only for source files that have changed if the isolatedModules option is true.
Transformers do not receive ts.Program instances at this time. Therefore, ts-macros cannot work.

Describe the solution you'd like
Is it possible to implement an optionally receive program parameters from Transformers and cache the first compilation type info results?
Maybe the direction of my question itself is wrong. I'm sorry in advance.

Describe alternatives you've considered
none

Additional context
I'm not sure if this is just a gulp-typescript's incremental compilation problem.

[FEATURE] Provide a convenient way to generate an export with a specific desired identifier.

Is your feature request related to a problem? Please describe.
I am writing a macro which should, among other things, export an identifier specified in the macro call. I can't seem to find a way to do this. In particular, function bodies are not allowed to export identifiers, so it seems I need to use the $$ts builtin macro, but if I write e.g.

$$ts!(`export const ${name} =  ${desired_value};`)

the identifier is mangled presumably for hygienic reasons. I suspect that I could use $$raw to do this, but it is hard to find good documentation on how to use factory.createExportAssignment or factory.createExportDeclaration. If you think it's best to do this with $$raw rather than add a facility for exporting, I respect that but if you have any pointers to information about how to use the Compiler API for something like this they would be welcome.

Describe the solution you'd like
I could imagine several designs, but I think the easiest to use would be another flag on $$define which specifies whether to export the definition, defaulting to false. Another possibility would be an "unhygienic" option to $$ts. Yet a third option would be an explicit $$export built-in macro.

Describe alternatives you've considered
I believe these are covered above.

Additional context
If a PR would help make this happen I am willing to try even though I am almost completely unfamiliar with the TypeScript Compiler API. But for this, I'd need an indication of the desired design for allowing exports. And if you have any suggestions for useful resources to get intformation about the TypeScript API it would be appreciated.

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.