Skip to content

Commit

Permalink
Refactor code-style
Browse files Browse the repository at this point in the history
  • Loading branch information
wooorm committed Sep 23, 2023
1 parent 552d7a8 commit 67a2700
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 95 deletions.
6 changes: 4 additions & 2 deletions lib/find-license.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {promises as fs} from 'node:fs'
import fs from 'node:fs/promises'

const licenseRegexp = /^licen[cs]e(?=$|\.)/i

/**
* @param {string} base
* @returns {Promise<string|undefined>}
* Folder.
* @returns {Promise<string | undefined>}
* Basename of license file.
*/
export async function findLicense(base) {
const files = await fs.readdir(base)
Expand Down
44 changes: 34 additions & 10 deletions lib/find-nearest-package.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,49 @@
/**
* @typedef {import('vfile').VFile} VFile
*/

/**
* @typedef Info
* Info.
* @property {string} folder
* Path to folder with `package.json`.
* @property {string | undefined} license
* SPDX identifier.
* @property {string | undefined} name
* Name of author.
* @property {string | undefined} url
* URL for author.
*/

import fs from 'node:fs/promises'
import path from 'node:path'
import parse from 'parse-author'
import {read} from 'to-vfile'
import {findUp} from 'vfile-find-up'

/**
* @param {string} base
* @returns {Promise<{license: string|undefined, name: string|undefined, url: string|undefined}>}
* @param {VFile} from
* File to resolve from.
* @returns {Promise<Info | undefined>}
* Info.
*/
export async function findNearestPackage(base) {
export async function findNearestPackage(from) {
/* c8 ignore next -- else is for stdin, typically not used. */
const base = from.dirname ? path.resolve(from.cwd, from.dirname) : from.cwd
const file = await findUp('package.json', base)

if (file) {
await read(file)
file.value = await fs.readFile(file.path)
/** @type {import('type-fest').PackageJson} */
const json = JSON.parse(String(file))
const author =
typeof json.author === 'string' ? parse(json.author) : json.author || {}

return {license: json.license, name: author.name, url: author.url}
return {
/* c8 ignore next -- always defined. */
folder: file.dirname ? path.resolve(file.cwd, file.dirname) : file.cwd,
license: json.license,
name: author.name,
url: author.url
}
}

// V8 bug on Node 12.
/* c8 ignore next 2 */
return {license: undefined, name: undefined, url: undefined}
}
147 changes: 67 additions & 80 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,102 +1,89 @@
/**
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').Paragraph} Paragraph
* @typedef {import('mdast').Link} Link
* @typedef {import('mdast').Paragraph} Paragraph
* @typedef {import('mdast').PhrasingContent} PhrasingContent
* @typedef {import('mdast').Root} Root
*
* @typedef {import('vfile').VFile} VFile
*/

/**
* @typedef Options
* Configuration (optional).
* @property {string} [name]
* License holder.
* Detected from the `package.json` closest to the file supporting both
* `object` and `string` format of `author`.
* *Throws when neither given nor detected.*
* @property {string} [license]
* SPDX identifier
* Detected from the `license` field in the `package.json` in the current
* working directory.
* Deprecated license objects are not supported.
* *Throws when neither given nor detected.*
* @property {string} [file]
* File name of license file
* Detected from the files in the directory of the `package.json` if there is
* @property {boolean | null | undefined} [ignoreFinalDefinitions=true]
* Ignore definitions at the end of the license section (default: `true`).
* @property {string | null | undefined} [file]
* Path to license file (optional);
* detected from the files in the directory of the `package.json` if there is
* one, or the current working directory, in which case the first file
* matching `/^licen[cs]e(?=$|\.)/i` is used.
* If there is no given or found license file, but `options.license` is a
* matching `/^licen[cs]e(?=$|\.)/i` is used;
* if there is no given or found license file, but `options.license` is a
* known SPDX identifier, the URL to the license on `spdx.org` is used.
* @property {string} [url]
* URL to license holder
* Detected from the `package.json` in the current working directory,
* supporting both `object` and `string` format of `author`.
* `http://` is prepended if `url` starts without HTTP or HTTPS protocol.
* @property {boolean} [ignoreFinalDefinitions=true]
* Ignore final definitions otherwise in the section.
* @property {string|RegExp} [heading]
* Heading to look for.
* Default: `/^licen[cs]e$/i`.
* @property {RegExp | string | null | undefined} [heading]
* Heading to look for (default: `/^licen[cs]e$/i`).
* @property {string | null | undefined} [license]
* SPDX identifier (optional, example: `'mit'`);
* detected from the `license` field in the `package.json` in the current
* working directory;
* throws when neither given nor detected.
* @property {string | null | undefined} [name]
* License holder (optional);
* detected from the `package.json` closest to the file supporting both
* `object` and `string` format of `author`;
* throws when neither given nor detected.
* @property {string | null | undefined} [url]
* URL to license holder (optional);
* detected from the `package.json` in the current working directory.
*/

import path from 'node:path'
import spdx from 'spdx-license-list'
import {findUp} from 'vfile-find-up'
import {headingRange} from 'mdast-util-heading-range'
import spdx from 'spdx-license-list'
import {findNearestPackage} from './find-nearest-package.js'
import {findLicense} from './find-license.js'

const licenseHeadingRegexp = /^licen[cs]e$/i
const http = 'http://'
const https = 'https://'
const licenseHeadingRegexp = /^licen[cs]e$/i

/** @type {Readonly<Options>} */
const emptyOptions = {}

/**
* Plugin to generate a license section.
* Generate a license section.
*
* @type {import('unified').Plugin<[Options], Root>}
* @param {Readonly<Options> | null | undefined} [options]
* Configuration (optional).
* @returns
* Transform.
*/
export default function remarkLicense(options = {}) {
const ignoreFinalDefinitions =
options.ignoreFinalDefinitions === undefined ||
options.ignoreFinalDefinitions === null
? true
: options.ignoreFinalDefinitions
const test = options.heading || licenseHeadingRegexp

export default function remarkLicense(options) {
const settings = options || emptyOptions
const ignoreFinalDefinitions = settings.ignoreFinalDefinitions !== false
const test = settings.heading || licenseHeadingRegexp

/**
* Transform.
*
* @param {Root} tree
* Tree.
* @param {VFile} file
* File.
* @returns {Promise<undefined>}
* Nothing.
*/
return async function (tree, file) {
// Else is for stdin, typically not used.
/* c8 ignore next */
const base = file.dirname ? path.resolve(file.cwd, file.dirname) : file.cwd
const packageFile = await findUp('package.json', base)
/** @type {string|undefined} */
let defaultName
/** @type {string|undefined} */
let defaultUrl
/** @type {string|undefined} */
let defaultLicense
/** @type {string|undefined} */
let defaultLicenseFile

// Skip package loading if we have all info in `options`.
if (!options.url || !options.name || !options.license) {
const result = await findNearestPackage(base)
defaultLicense = result.license
defaultName = result.name
defaultUrl = result.url
}

if (!options.file) {
defaultLicenseFile = await findLicense(
(packageFile &&
packageFile.dirname &&
path.resolve(packageFile.cwd, packageFile.dirname)) ||
file.cwd
)
}

const url = options.url || defaultUrl
const name = options.name || defaultName
const license = options.license || defaultLicense
let licenseFile = options.file || defaultLicenseFile

/* Ignore the license file itself. */
const info =
!settings.url || !settings.name || !settings.license || !settings.file
? await findNearestPackage(file)
: undefined
const url = settings.url || (info ? info.url : undefined)
const name = settings.name || (info ? info.name : undefined)
const license = settings.license || (info ? info.license : undefined)
let licenseFile =
settings.file ||
(await findLicense((info ? info.folder : undefined) || file.cwd))

// Ignore the license file itself.
if (licenseFile && file.path === licenseFile) {
return
}
Expand Down Expand Up @@ -125,11 +112,11 @@ export default function remarkLicense(options = {}) {
tree,
{ignoreFinalDefinitions, test},
function (start, _, end) {
/** @type {PhrasingContent[]} */
/** @type {Array<PhrasingContent>} */
const children = []
/** @type {Paragraph} */
const node = {type: 'paragraph', children}
/** @type {Paragraph|Link} */
/** @type {Link | Paragraph} */
let parent

if (licenseFile) {
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@
"mdast-util-heading-range": "^4.0.0",
"parse-author": "^2.0.0",
"spdx-license-list": "^6.0.0",
"to-vfile": "^8.0.0",
"unified": "^11.0.0",
"vfile": "^6.0.0",
"vfile-find-up": "^7.0.0"
},
"devDependencies": {
Expand Down
9 changes: 8 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ import {remark} from 'remark'
import semver from 'semver'
import license from '../index.js'

test('remark-license', async function (t) {
await t.test('should expose the public api', async function () {
assert.deepEqual(Object.keys(await import('../index.js')).sort(), [
'default'
])
})
})

test('fixtures', async function (t) {
// Prepapre.
const root = new URL('fixtures/', import.meta.url)
Expand Down Expand Up @@ -62,7 +70,6 @@ test('fixtures', async function (t) {

try {
const file = await remark()
// @ts-expect-error: to do: remove after update.
.use(license, config)
.process({
value: await fs.readFile(inputUrl),
Expand Down

0 comments on commit 67a2700

Please sign in to comment.