npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@kimono/yo-transform-filenames

v0.2.1

Published

Template logic for filenames

Downloads

13

Readme

@kimono/yo-filename-templates

A transform stream that supports a modified version of the EJS template syntax in names of template files and folders.

Modified EJS syntax

Due to reserved characters and words, some adjustments are necessary in order to use EJS template syntax in file and folder names.

There are several characters and constructs that you cannot use in their original form. You must use their supported alternatives instead. While it's a bit of a weird-looking syntax, it gets the job done and was the most elegant solution I could find.

Check out src/replacements.ts for an overview of constructs that are handled specially.

Angle brackets

In EJS syntax, you use angle brackets for defining all sorts of Tags. Because angle brackets are reserved and cannot be used in filenames, you have to use curly brackets:

  • <% becomes {%
  • %> becomes %}
  • <%=foo%> becomes {%=foo%}

Boolean syntax: binary

Because the pipe | is reserved and cannot be used in filenames, you have to use an alternative. For consistency reasons, the non-reserved & character requires you to use an alternative as well:

  • && becomes #++
  • || becomes #--
  • a && b becomes a #++ b
  • a && b || baz becomes a #++ b #-- baz

Boolean syntax: ternary

Because both ? and : are reserved and cannot be used in filenames, you have to use alternatives:

  • ? becomes #+
  • : becomes #-
  • a ? b : c becomes a #+ b #- c

Features

Please note that in the following examples, we assume that variables were provided as context data (via CLI argument, CLI option or as an answer to some inquirer question)

Camel-cased and Pascal-cased values

Many times you will want to use the same value for a filename and for code inside of that file. But that value may contain characters that are invalid in code identifiers, for example spaces or dashes.

For this reason, all context variables are provided to the templates in a camel-cased variant that has CC appended to its name, as well as in a pascal-cased variant that has PC appended to its name.

For example, given context data {projectName: 'my-project'}, your templates will have access to {projectName: 'my-project', projectNameCC: 'myProject', projectNamePC: 'MyProject'}.

Variable interpolation

Inject values into file and folder names.

  • Desired EJS template syntax: <%= filename %>.js
  • Supported template filename: {%= filename %}.js
  • Given the context {filename: 'foo'}, the file is copied as foo.js

Conditionally name files

  • Desired EJS template syntax: index<%= typescript ? ".ts" : ".js" %>
  • Supported template filename: index{%= typescript #+ ".ts" #- ".js" %}
  • Given the context {typescript: true}, the file is copied as index.ts
  • Given the context {typescript: false}, the file is copied as index.js

Conditionally copy or omit files

Template files with a boolean expression in their name will only be copied if that expression resolves truthy.

  • Desired EJS template syntax: <%= foo && "foo.json" %>
  • Supported template filename: {%= foo #++ "foo.json" %}
  • Given the context {foo: true}, the file will be copied to the target as foo.json
  • Given the context {foo: false}, the file will not be copied to the target at all

Recipes

Conditinal tsconfig.json

Only copy the tsconfig.json file when typescript was selected:

  • templates/src/{%= typescript #++ tsconfig.json %}

Dynamic file extension

Useful to decide for the proper file extension based on given user options.

  • e.g. templates/src/index.{%= typescript #+ "ts" #- "js" %}
  • e.g. templates/src/styles.{%= sass #++ "scss" #-- less #++ "less" #-- "css" %}

javascript entry file and main file

For a package generator, you could provide an index file that has a static name, and a main file that has a dynamic name - the name of the package itself.

The function definition in the main file uses the camel-cased variable to avoid syntax errors, e.g. fetch-users becomes fetchUsers:

Template file templates/src/index.js:
export { default } from './<%=packageName%>';
Template file templates/src/{%=packageName%}.js
export default function <%=packageNameCC%>() {
    return [];
}

Typescript/Javascript component template

You could provide a template that supports a typescript option. You would then copy the template files as .js or .ts/.tsx accordingly, and with either propTypes or an interface defined in the component.

Template file templates/src/index.{%= typescript #+ "ts" #- "js" %}:
export { default } from './<%=packageName%>';
Template file templates/src/{%=packageName%}.{%= typescript #+ "tsx" #- "js" %}
import React from 'react';
<%if (!typescript) { %>import PropTypes from 'prop-types';<% } %>
import cx from 'classnames';

<%if (typescript) { %>
export interface <%=packageNamePC%>Props {
    children?: React.ReactChild;
    className?: string;
}
<% } else { %>
<%=packageNamePC%>.propTypes = {
    children: PropTypes.node,
    className: PropTypes.string,
};
<% } %>
<% if (!typescript) { %>export default function <%=packageNamePC%>({ children, className, ...props }) {<% } %>
<% if (typescript) { %>export default function <%=packageNamePC%>({ children, className, ...props }: <%=packageNamePC%>Props): React.ReactElement {<% } %>
    return (
        <div className={cx('<%=packageNamePC%>', className)} {...props}>
            {children}
        </div>
    );
};

Javascript output

Given the context {packageName: 'my-react-component', typescript: false}, the resulting files will be:

Output file src/index.ts:
export { default } from './my-react-component';
Output file src/my-react-component.js
import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';

MyReactComponent.propTypes = {
  children: PropTypes.node,
  className: PropTypes.string
};
export default function MyReactComponent({ children, className, ...props }) {
  return (
    <div className={cx('MyReactComponent', className)} {...props}>
      {children}
    </div>
  );
}

Typescript output

Given the context {packageName: 'my-react-component', typescript: true}, the resulting files will be:

Output file src/index.ts:
export { default } from './my-react-component';
Output file src/my-react-component.tsx
import React from 'react';
import cx from 'classnames';

export interface MyReactComponentProps {
  children: React.ReactChild;
  className?: string;
}
export default function MyReactComponent({ children, className, ...props }: MyReactComponentProps): React.ReactElement {
  return (
    <div className={cx('MyReactComponent', className)} {...props}>
      {children}
    </div>
  );
}