Coder Social home page Coder Social logo

code-block-writer's Introduction

code-block-writer

npm version CI JSR stable

Code writer for JavaScript and TypeScript code.

With Deno:

deno add jsr:@david/code-block-writer

Or with Node:

npm install --save code-block-writer

Example

// import CodeBlockWriter from "code-block-writer"; // for npm
import CodeBlockWriter from "@david/code-block-writer";

const writer = new CodeBlockWriter({
  // optional options
  newLine: "\r\n",         // default: "\n"
  indentNumberOfSpaces: 2, // default: 4
  useTabs: false,          // default: false
  useSingleQuote: true     // default: false
});

writer.write("class MyClass extends OtherClass").block(() => {
  writer.writeLine(`@MyDecorator(1, 2)`);
  writer.write(`myMethod(myParam: any)`).block(() => {
    writer.write("return this.post(").quote("myArgument").write(");");
  });
});

console.log(writer.toString());

Outputs (using "\r\n" for newlines):

class MyClass extends OtherClass {
  @MyDecorator(1, 2)
  myMethod(myParam: any) {
    return this.post('myArgument');
  }
}

Methods

  • block(block?: () => void) - Indents all the code written within and surrounds it in braces.
  • inlineBlock(block?: () => void) - Same as block, but doesn't add a space before the first brace and doesn't add a newline at the end.
  • getLength() - Get the current number of characters.
  • writeLine(text: string) - Writes some text and adds a newline.
  • newLine() - Writes a newline.
  • newLineIfLastNot() - Writes a newline if what was written last wasn't a newline.
  • blankLine() - Writes a blank line. Does not allow consecutive blank lines.
  • blankLineIfLastNot() - Writes a blank line if what was written last wasn't a blank line.
  • quote() - Writes a quote character.
  • quote(text: string) - Writes text surrounded in quotes.
  • indent(times?: number) - Indents the current line. Optionally indents multiple times when providing a number.
  • indent(block?: () => void) - Indents a block of code.
  • space(times?: number) - Writes a space. Optionally writes multiple spaces when providing a number.
  • spaceIfLastNot() - Writes a space if the last was not a space.
  • tab(times?: number) - Writes a tab. Optionally writes multiple tabs when providing a number.
  • tabIfLastNot() - Writes a tab if the last was not a tab.
  • write(text: string) - Writes some text.
  • conditionalNewLine(condition: boolean) - Writes a newline if the condition is matched.
  • conditionalBlankLine(condition: boolean) - Writes a blank line if the condition is matched.
  • conditionalWrite(condition: boolean, text: string) - Writes if the condition is matched.
  • conditionalWrite(condition: boolean, textFunc: () => string) - Writes if the condition is matched.
  • conditionalWriteLine(condition: boolean, text: string) - Writes some text and adds a newline if the condition is matched.
  • conditionalWriteLine(condition: boolean, textFunc: () => string) - Writes some text and adds a newline if the condition is matched.
  • setIndentationLevel(indentationLevel: number) - Sets the current indentation level.
  • setIndentationLevel(whitespaceText: string) - Sets the current indentation level based on the provided whitespace text.
  • withIndentationLevel(indentationLevel: number, action: () => void) - Sets the indentation level within the provided action.
  • withIndentationLevel(whitespaceText: string, action: () => void) - Sets the indentation level based on the provided whitespace text within the action.
  • getIndentationLevel() - Gets the current indentation level.
  • queueIndentationLevel(indentationLevel: number) - Queues an indentation level to be used once a new line is written.
  • queueIndentationLevel(whitespaceText: string) - Queues an indentation level to be used once a new line is written based on the provided whitespace text.
  • hangingIndent(action: () => void) - Writes the code within the action with hanging indentation.
  • hangingIndentUnlessBlock(action: () => void) - Writes the code within the action with hanging indentation unless a block is written going from the first line to the second.
  • closeComment() - Writes text to exit a comment if in a comment.
  • unsafeInsert(pos: number, text: string) - Inserts text into the writer. This will not update the writer's state. Read more in its jsdoc.
  • isInComment() - Gets if the writer is currently in a comment.
  • isAtStartOfFirstLineOfBlock() - Gets if the writer is currently at the start of the first line of the text, block, or indentation block.
  • isOnFirstLineOfBlock() - Gets if the writer is currently on the first line of the text, block, or indentation block.
  • isInString() - Gets if the writer is currently in a string.
  • isLastNewLine() - Gets if the writer last wrote a newline.
  • isLastBlankLine() - Gets if the writer last wrote a blank line.
  • isLastSpace() - Gets if the writer last wrote a space.
  • isLastTab() - Gets if the writer last wrote a tab.
  • getLastChar() - Gets the last character written.
  • endsWith(text: string) - Gets if the writer ends with the provided text.
  • iterateLastChars<T>(action: (char: string, index: number) => T | undefined): T | undefined - Iterates over the writer's characters in reverse order, stopping once a non-null or undefined value is returned and returns that value.
  • iterateLastCharCodes<T>(action: (charCode: number, index: number) => T | undefined): T | undefined - A slightly faster version of iterateLastChars that doesn't allocate a string per character.
  • getOptions() - Gets the writer options.
  • toString() - Gets the string.

Other Features

  • Does not indent within strings.
  • Escapes newlines within double and single quotes created with .quote(text).

C# Version

See CodeBlockWriterSharp.

code-block-writer's People

Contributors

dependabot[bot] avatar dgetu avatar dsherret avatar lazarljubenovic 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

code-block-writer's Issues

Add indent function

Would indent the current line by the indent option.

For example:

writer.indent().write("test").newLine().write("test");

Would output:

    test
test

Add `indent(times)`

I'm actually kind of confused about why I have a tab(times) method, but not an indent(times) method.

I kind of feel like the tab method shouldn't exist because people should use indent instead.

Allow consecutive blank lines

The person using this library should just be wary about writing consecutive blank lines. Right now it's difficult to write many blank lines and that might be desirable.

Iterative writes

Similarly to the reason conditionalWrite was introducted, it would be really nice if we were not forced to break the chain and introduce a for-of loop with a single writeLine call.

Something along the lines of the following would be great.

const indexes = [5, 9, 10, 15, 17]

writer
  .writeLine(`return [`)
  .iterateWriteLine(indexes, index => `this.array[${index}],`)
  .writeLine(`]`)

setIndentationLevel

Allows setting the current indentation level.

A number could be passed in or the current indentation as text.

  • Will throw if the text passed in is not whitespace.
  • If current is tabs and text passed in is spaces, then it will assume 4 spaces per tab.
  • Spaces passed in will be counted then rounded to the nearest number divisible by the number of spaces per indent.
  • If spaces and tabs are passed in, then it will count all the spaces and round the them to the nearest number divisible by the number of spaces per indent. Ex. 2 tabs + 10 spaces (with 4 spaces per indent) = indentation level of 4

Add .quote()

const writer = new CodeBlockWriter({ useSingleQuote: true });

writer.quote(); // outputs: '
writer.quote("str"); // outputs: 'str'

This would be useful because then the quote style would be defined within the writer and code could be written the same regardless of the quote style.

Add insert method

I've finally come across a scenario that I need this. Just work from the back of the array to find the position to insert at.

Add closeComment()

This will close the comment if isInComment() is true.

Newline for // comments and */ for others. For */ it should do a space if there is a non-whitespace character in front of it on the current line.

Stays in string even after newline?

Seems like it's staying in the string even after the newline (this was obviously a mistake, but this is a bug nonetheless):

    function printCommaListExpression(node: ts.CommaListExpression) {                             
        writer.write("ts.createCommaList(").newLine();                                            
        writer.indentBlock(() => {                                                                
            "[\n" + "\n    " + node.elements.map(item => getNodeText(item)).join(",\n    ) + "\n]"
});                                                                                               
writer.write(");");                                                                               
}                                                                                                 

Do not do a newline unless writing after a block

For example the following:

writer.block(() => {});

Should output: "{\n}"

But if you do the following:

writer.block(() => {}).write("t");

It will do the newline after the block: "{\n}\nt"

Reason: I found if doing a block it would be annoying that it would automatically add a newline. This meant I would end up with code that looked like this:

function myFunc() {
    func inner() {
    }

}

TypeError: CodeBlockWriter is not a constructor

Hey there,

I have installed the library using the command npm install --save code-block-writer

I have created a file main.js then added the example mentioned in readme.

main.js file


const CodeBlockWriter = require("code-block-writer");

const writer = new CodeBlockWriter({
    // optional options
    newLine: "\r\n",         // default: "\n"
    indentNumberOfSpaces: 2, // default: 4
    useTabs: false,          // default: false
    useSingleQuote: true     // default: false
});

writer.write("class MyClass extends OtherClass").block(() => {
    writer.writeLine(`@MyDecorator(1, 2)`);
    writer.write(`myMethod(myParam: any)`).block(() => {
        writer.write("return this.post(").quote("myArgument").write(");");
    });
});

console.log(writer.toString());

then to run it I did node main.js

I am getting an error :


const writer = new CodeBlockWriter({
               ^

TypeError: CodeBlockWriter is not a constructor
    at Object.<anonymous> (A:\practise\main.js:3:16)
    at Module._compile (node:internal/modules/cjs/loader:1159:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
    at Module.load (node:internal/modules/cjs/loader:1037:32)
    at Module._load (node:internal/modules/cjs/loader:878:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:23:47

Node.js v18.12.1

Sorry I never used JS/TS . So can you please tell me how to use it correctly.

Writing a newline should indent the text that follows

It will slow it down a bit, but doing writer.write("my string\ntest") should:

  1. Change \n or \r\n to the newline character specified when creating the writer
  2. Add the correct amount of indentation after the newline.

Essentially, it should just take the inputted string and do:

(str || "").split(/\r?\n/).forEach(s => this.writeLine(s));

Add in future maybe: a function called writeWithoutAutoIndent(str: string) for people to use to bypass this functionality?

Release for Deno

This could be easily released for Deno. Probably it just needs to be bundled into a single file for release, but the best way to support both Node and Deno should be investigated.

TypeScript error TS2351: This expression is not constructable.

Steps to reproduce:

  1. npm install --save code-block-writer
  2. Add this file to your TypeScript project. (I named mine CodeBlockWriter.mts, to leverage TypeScript's ECMAScript module support.)
import CodeBlockWriter from "code-block-writer";

it("CodeBlockWriter can be assembled", () => {
  const writer = new CodeBlockWriter;
  expect(writer).toBeTruthy();
});
  1. Invoke tsc for this file.
_00_shared_utilities/spec/CodeBlockWriterTest.mts(4,22): error TS2351: This expression is not constructable.
  Type 'typeof import("/home/ajvincent/code-generation/cross-stitch/node_modules/code-block-writer/types/mod")' has no construct signatures.

Please advise.

TypeScript version 4.7.4
NodeJS version 16.14.0

Improve Performance

I finally got around to doing some profiling and ts-morph is really slow when printing because code-block-writer is slow. When printing it spends 52% of its time in the writeIndividual function within code-block-writer.

image

Property generic types

If I have a class with a property, then I would like to get an array of all types with generic.

Example:

class A {
  private test: GenericType<AnotherType>>;
}

I would like to be able to call:
classDeclration.getProperties()[0].getType().getAllTypes() and return ['GenericType', 'AnotherType'] - could not find it in documentation and tried many methods from Type interface. In the end I am processing compilerNode for it

Add method for iterating over the previously written characters

Right now I'm using toString() in some areas of ts-morph in order to check the previously written characters. This is obviously going to be slow, so it would be better if there were a way to iterate over the past characters.

I'm not sure how this would look though.

Error when running `yarn test`

Just cloned the repo and ran tests. They passed, but it ended with the following message

/home/lazar/code-block-writer/node_modules/coveralls/bin/coveralls.js:18
        throw err;
        ^
Bad response: 422 {"message":"Couldn't find a repository matching this job.","error":true}
error An unexpected error occurred: "Command failed.
Exit code: 1
Command: sh
Arguments: -c gulp test && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
Directory: /home/lazar/code-block-writer
Output:
".

Not sure what the whole thing is about so my only guess is that you're using some third party service and I don't have access to it?

I tried opening ./coverage/lcov.info but have no idea what that's about 😅

Suggestion: writer.pairedWrite(startToken, endToken, newLine, indent, block: () => void)

I like block(), quote(), etc. But there are other pairings of tokens which are important: parentheses, square brackets, backticks for template strings, etc.

Suggested code:

  pairedWrite(
    startToken: string,
    endToken: string,
    newLine: boolean,
    indent: boolean,
    block: () => void
  ) : void
  {
    this.write(startToken);
    if (newLine) this.newLine();
    indent ? this.indent(block) : block();
    if (newLine) this.newLine();
    this.write(endToken);
  }

Paired writes could also be useful for (// #region ..., // #endregion ...) cases.

Declaration and capturing of variables

Hi! Thank you for this library!

Is there any way to manage scopes of variables with it? For example, if I do some recursive code generation, at the moment I need to manage scope separately (mostly for capturing outer scope's variables).

Let's assume I generate some matrix processing code, and want to have some recursive function which generates the code. In that case I need to capture previous loop index somewhere, to then access it in the inner loop (and not use same i variable name in the inner loop as well):

for (var i = 0; i < 10; i++) {
  for (var i0 = 0; i < 10; i++) {
    // access here both i and i0
  }
}

It would be nice to have such feature in the library since it already is managing blocks and other scope-related things.

Sorry if expression of my thoughts is a bit messy, but I hope the issue is clear :D

conditionalWrite

I find myself doing this a lot:

if (someCondition) {
    this.writer.write("something");
}

This just takes up a lot of vertical room—especially since I usually add a blank line before and after if statements. It would be nicer to add this:

this.writer.conditionalWrite(someCondition, "something");

Lazily evaluate conditional writes

Currently, the following throws (naturally) if foo is null.

this.conditionalWrite(foo != null, foo.bar)

However, this would not throw if the function was evaluated only after the conditional check has been passed:

this.conditionalWrite(foo != null, () => foo.bar)

We could still support the old one, of course, so it can be done as a non-breaking change.

getBaseTypes() and getBaseClass() not working

I'm using the latest version of ts-morph, Node 16, and typescript 4.3.5

Here's the source code of the class file

import React from 'react';

export class ClassComp extends React.Component {}

And here's the test code:

  let exportedClasses: ClassDeclaration[] = project
    .getSourceFiles()
    .flatMap((file) => file.getClasses())
    .filter((c) => c.isExported());

  console.log(`Found ${exportedClasses.length} exported classes`);

  exportedClasses.forEach((c) => {
    console.log(
      `   Checking class ${c.getName()}
      base types count: ${c.getBaseTypes().length}
      Base class: ${c.getBaseClass()}
      Extends: ${c.getExtends().getText()}`,
    );
  });

Output:

Found 1 exported classes
   Checking class ClassComp
      base types count: 0
      Base class: undefined
      Extends: React.Component

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.