Extract code snippets from source files.
Snippets.js helps you extract code snippets from source files so you can use them as text in your own projects.
It’s particularly useful in documentation projects, where you need to showcase pieces of code but can’t either run or lint them to ensure they’re correct. Snippets.js integrates in your workflow and returns a collection of parsed snippets.
WARNING: The API is still unstable. Do not use in production yet.
npm install snippets.js
# or
yarn add snippets.js
Snippets.js iterates over your source files, generates snippet objects, and exposes them through a readable stream.
const { createSnippets } = require("snippets.js");
const snippets = createSnippets({
sourceDir: "path/to/snippets/*",
ignore: ["node_modules/*"]
});
snippets.on("data", snippet => {
console.log(snippet);
});
snippets.on("end", () => {
console.log("Finished parsing snippets!");
});
By default, Snippets.js parses the file extension and sets it as the snippet’s language. You can change it in the languages
setting.
const snippets = createSnippets({
// ...
languages: [
{
fileType: ["rb"],
language: "ruby"
}
]
});
You can also transform the raw input code. This is useful when you need to remove a piece of code (think <?php
in PHP snippets, for instance).
const snippets = createSnippets({
// ...
languages: [
{
// ...
transform: code => code.replace("# frozen_string_literal: true", "")
}
]
});
If you only need to parse a chunk of a longer snippet, you can add special comments to delimit the part you need.
Comments work with //
and #
.
public class Factorial {
public static void main(String[] args) {
int num = 10;
long factorial = 1;
// snippets-start
for(int i = 1; i <= num; ++i)
{
factorial *= i;
}
// snippets-end
System.out.printf("Factorial of %d = %d", num, factorial);
}
}
num = 10
factorial = 1
# snippets-start
for i in range(1,num+1):
factorial = factorial * i
# snippets-end
print "Factorial of %s = %d" % (num,factorial)
You can create several commented parts in a single snippet to chunk it into several snippet objects.
Additionally, you can name snippets by suffixing the start comment with :<your-name>
. Snippet names can include letters, numbers, hyphens and underscores.
Note: Overlapping and nested comments aren’t supported.
num = 10
factorial = 1
# snippets-start
for i in range(1,num+1):
factorial = factorial * i
# snippets-end
# snippets-start:my-snippet
print "Factorial of %s = %d" % (num,factorial)
# snippets-end
The library exposes two main modules: createSnippets
and createSnippet
.
They both let you generate Snippet
objects.
createSnippets
Type: (config: { sourceDir?, ignore?, languages? }) => NodeJS.ReadableStream
The createSnippets
factory lets you generate a readable stream of Snippet
objects from source files.
You can use it to log objects to the console, write them to a file, store them in memory for later usage, anything.
const { createSnippets } = require("snippets.js");
const snippets = createSnippets({
sourceDir: "path/to/snippets/*",
ignore: ["node_modules/*"],
languages: [
{
fileType: ["rb"],
language: "ruby",
transform: code => code.replace("# frozen_string_literal: true", "")
}
]
});
snippets.on("data", snippet => {
console.log(snippet);
});
snippets.on("end", () => {
console.log("Finished parsing snippets!");
});
sourceDir?
Type: string | undefined
The path where to find the source snippets. Supports glob patterns).
module.exports = {
sourceDir: "path/to/snippets/**/*"
};
ignore?
Type: string[] | undefined
The paths to ignore. Supports glob patterns).
module.exports = {
ignore: ["node_modules"]
};
languages?
Type: { fileType, language?, transform? }[] | undefined
A collection of rules to handle languages. This lets you apply specific treatment to each snippet based on file type.
module.exports = {
languages: [
{
fileType: ["rb", "erb"],
language: "ruby",
transform(code) {
return code.replace("# frozen_string_literal: true", "");
}
},
{
// ...
}
]
};
languages.fileType
Type: string[]
The file type(s) on which to apply the rule (based on file extension).
module.exports = {
languages: [
{
fileType: ["rb", "erb"]
}
]
};
languages.language?
Type: string | undefined
The language slug to assign to the snippet. This is used as language for the Markdown fenced blocks.
If not specified, defaults to the file extension.
module.exports = {
languages: [
{
language: "ruby"
}
]
};
languages.transform?
Type: (code: string) => string | undefined
A transform function to apply on the code.
module.exports = {
languages: [
{
transform(code) {
return code.replace("# frozen_string_literal: true", "");
}
}
]
};
createSnippet
Type: (options: { language, path?, code, transform? }) => Snippet
The createSnippet
factory lets you generate Snippet
objects from source input. This is what the library uses internally to generate snippets from your source files.
const { createSnippet } = require("snippets.js");
const phpSnippet = createSnippet({
name: "my-snippet",
language: "php",
code: '<?php\necho "Hello world!"',
transform: code => code.replace("<?php", "")
});
You can use createSnippet
to manually generate snippets as in the example above, or to build your own snippet factories for specific languages.
const createPhpSnippet = code =>
createSnippet({
language: "php",
code,
transform: code => code.replace("<?php", "")
});
const code = '<?php\necho "Hello world!"';
const phpSnippet = createPhpSnippet(code);
If you’re using TypeScript, you can implement the SnippetFactory
interface.
const createPhpSnippet: SnippetFactory = code =>
createSnippet({
language: "php",
code,
transform: code => code.replace("<?php", "")
});
options.name?
Type: string | undefined
The name of the snippet.
options.language
Type: string
The language of the snippet.
options.path?
Type: string | undefined
The path of the snippet’s source file. This is only useful when you’re parsing snippets from source files, and can be ommitted if you’re building Snippet
objects by hand.
options.code
Type: string
The raw code of the snippet.
options.transform?
Type: (code: string) => string | undefined
A function to transform the raw code before returning it.
This is useful when you want to get rid of a piece of code that’s necessary in the source file, but that you don’t need in the final snippet.
Snippet
A Snippet
object contains all the information about a code snippet. This is what the library returns you when you’re either using createSnippet
manually, or listening for data on snippets
.
If you’re using TypeScript, you can implement the Snippet
interface.
const snippet: Snippet = createSnippet({
language: "php",
code: '<?php\necho "Hello world!"',
transform: code => code.replace("<?php", "")
});
name
Type: string | undefined
Get the name of a snippet.
snippet.name; // 'my-snippet'
language
Type: string
Get the language of a snippet.
snippet.language; // 'php'
path?
Type: string | undefined
Get the path of a snippet.
This is the original path to the source file. This is only useful when you’re parsing snippets from source files, and can be ommitted if you’re building Snippet
objects by hand.
snippet.path; // 'path/to/original/snippet.php'
code
Type: string
Get the code of a snippet.
snippet.code; // 'echo "Hello world!"'
markdown
Type: string
Get the code of a snippet in Markdown format.
snippet.markdown; // '```php\necho "Hello world!"\n```'
Snippets.js was built to work with many snippets, from source files. Reading through a bunch of files and storing lots of objects that potentially contain long chunks of code can become greedy in terms of memory. Streams are an ideal solution to make sure that memory consumption remains under control.
If you don’t want to use streams, you can reimplement the iteration logic as you see fit and consume the createSnippet
factory exposed by the library.
Sure! This monorepo exposes a fully working example in packages/example/
. Check out the code for inspiration, and follow the README to run the demo.
Snippets is licensed under MIT.