Cq
extract code snippets using selectors (instead of line numbers) - ES5, ES6, TypeScript
Install / Use
/learn @newlinedotco/CqREADME
cq: Code Query

A query language and toolkit to query lines of code for blogs and documentation - without copying and pasting or using manual line numbers
Supports JavaScript ES5, ES6, JSX, and TypeScript as well as any Treesitter language: Python, Ruby, Rust, C, Java, etc.
cqsupports sophisticated, production-ready selectors and is used for all of the newline BooksIf you're a developer and you're interested in writing a programming book, but you're not sure where to start, then read here
Online Demo
cq Suite
cq- The core cq library -- given a code string and a query, returns the lines of codecqmd- CLI tool to pre-process markdown withcq. (Used to generate the current README)remark-cq- a remark (rehype-compatible) plugin to slurp code snippets with cq - (e.g. load code snippets into Docusaurus)cq-treesitter-engine- an engine for using treesitter with cq, meaning you can query any language treesitter supports (Python, Rust, C, Java, Ruby)
Install
$ npm install --global @fullstackio/cq
Usage
$ cq <query> <file>
# or
$ cat file | cq <query>
Examples
Say we have a file examples/basics.js with the following code:
const bye = function() {
return 'bye';
}
bye(); // -> 'bye'
let Farm = () => 'cow';
class Barn {
constructor(height, width) {
this.height = height;
this.width = width;
}
calcArea() {
return this.height * this.width;
}
}
Get the bye() function:
Query:
$ cq '.bye' examples/basics.js
Result:
const bye = function() {
return 'bye';
}
Get the calcArea() function on the Barn class:
Query:
$ cq '.Barn .calcArea' examples/basics.js
Result:
calcArea() {
return this.height * this.width;
}
Get the bye() function plus the line after:
This example uses an operator context.
The API is: context(identifier, linesBefore, linesAfter)
Query:
$ cq 'context(.bye,0,1)' examples/basics.js
Result:
const bye = function() {
return 'bye';
}
bye(); // -> 'bye'
Get the range of constructor through calcArea, inclusive, of the Barn class
$ cq '.Barn .constructor-.calcArea' examples/basics.js
constructor(height, width) {
this.height = height;
this.width = width;
}
calcArea() {
return this.height * this.width;
}
json flag
If you pass --json you'll get the results in JSON, which can be useful for further processing:
$ cq --json 'context(.bye,0,1)' examples/basics.js
{
"code": "const bye = function() {\n return 'bye';\n}\nbye(); // -> 'bye'",
"start": 598,
"end": 659,
"start_line": 25,
"end_line": 28
}
TypeScript Support
cq works with TypeScript as well. Say we had the following TypeScript File AuthService.ts:
import {Injectable, provide} from '@angular/core';
@Injectable()
export class AuthService {
login(user: string, password: string): boolean {
if (user === 'user' && password === 'password') {
localStorage.setItem('username', user);
return true;
}
return false;
}
logout(): any {
localStorage.removeItem('username');
}
getUser(): any {
return localStorage.getItem('username');
}
isLoggedIn(): boolean {
return this.getUser() !== null;
}
}
export var AUTH_PROVIDERS: Array<any> = [
provide(AuthService, {useClass: AuthService})
];
Get the AUTH_PROVIDERS export:
Query:
$ cq '.AUTH_PROVIDERS' examples/AuthService.ts
Result:
export var AUTH_PROVIDERS: Array<any> = [
provide(AuthService, {useClass: AuthService})
];
Get the isLoggedIn() function through AUTH_PROVIDERS
Query:
$ cq '(.AuthService .isLoggedIn)-.AUTH_PROVIDERS' examples/AuthService.ts
Result:
isLoggedIn(): boolean {
return this.getUser() !== null;
}
}
export var AUTH_PROVIDERS: Array<any> = [
provide(AuthService, {useClass: AuthService})
];
Searching for strings
cq can search for strings as well as identifiers. Say we have the following test:
import chai from 'chai';
const assert = chai.assert;
describe('My First Test', () => {
it('basic assert', () => {
assert.equal(1, 1);
});
});
describe('My Second Test', () => {
it('basic assert', () => {
// this is the second one
assert.equal(2, 2);
});
});
We can get the first test:
Query:
$ cq "'My First Test'" examples/mocha.test.js
Result:
describe('My First Test', () => {
it('basic assert', () => {
assert.equal(1, 1);
});
});
Or get the it block in the second test:
Query:
$ cq "'My Second Test' 'basic assert'" examples/mocha.test.js
Result:
it('basic assert', () => {
// this is the second one
assert.equal(2, 2);
});
comments() operator
Sometimes we want to pull the comments before a selection. cq supports this using the comments() operator:
File comments.js:
function hello() {
return 'hi';
}
/*
* @function bye
*/
function bye() {
return 'see ya';
}
Get the bye() function with comments:
Query:
$ cq 'comments(.bye)' comments.js
Result:
/*
* @function bye
*/
function bye() {
return 'see ya';
}
This file was itself generated using
cq.See many more examples in the
/examplesdirectory
Features
- Extract chunks of code from text using robust selectors (vs. brittle line numbers)
- Locate ranges of code using identifiers
- Parses ES6 & JSX (with babylon)
- Parses TypeScript
Operators
cq supports a number of operators that modify the selection:
Motivation
When writing blog posts, tutorials, and books about programming there's a tension between code that gets copied and pasted into the text and runnable code on disk.
If you copy and paste your code into the copy, then you're prone to typos, missing steps. When things change, you have to update all of the copypasta and eyeball it to make sure you didn't miss anything. Mistakes are really easy to make because you can't really test code that's in your manuscript without it's context.
A better solution is to keep your code (or steps of your code) as runnable examples on disk. You can then load the code into your manuscript with some pre-processing.
The problem with the code-on-disk approach is how to designate the ranges of code you wish to import. Line numbers are the most obvious approach, but if you add or remove a line of code, then you have to adjust all line numbers accordingly.
cq is a tool that lets you specify selectors to extract portions of code. Rather than using brittle line numbers, instead cq lets you query your code. It uses babylon to understand the semantics of your code and will extract the appropriate lines.
Query Grammar
.Identifier
Examples:
.Simple.render
A dot . preceding JavaScript identifier characters represents an identifier.
In this code:
const Simple = React.createClass({
render() {
return <div>{this.renderName()}</div>;
}
});
The query .Simple would find the whole const Simple = ... variable declaration.
Searches for identifiers traverse the whole tree, relative to the parent, and return the first match. This means that you do not have to start at the root. In this case you could query for .render and would receive the render() function. That said, creating more specific queries can help in the case where you want to disambiguate.
[space]
Examples:
.Simple .render.foo .bar .baz
The space in a query selection expression designates a parent for the next identifier. For instance, the query .Simple .render will first look for the identifier Simple and then find the render function that is a child of Simple.
The space indicates to search for the next identifier anywhere within the parent. That is, it does not require that the child identifier be a direct child the parent.
In this way the space is analogous to the space in a CSS selector. E.g. search for any child that matches.
cqdoes not yet support the>notation (which would require the identifier to be a direct child), but we may in the future.
You can write child selection in parenthesis () if there is ambiguity. E.g.: (.foo .bar) .
Range
Examples:
.constructor-.calcArea.Barn .constructor-.calcArea1-(.AuthService .login).foo-EOF
Given:
class Barn {
constructor(height, width) {
this.height = height;
this.width = width;
}
calcArea() {
return this.height * this
