Add (now-working) handling of all values of @typelien encountered in links in "Code de procédure pénale"
This commit is contained in:
parent
5e7f027cd5
commit
f436b6b11a
1 changed files with 397 additions and 8 deletions
|
@ -3,24 +3,322 @@ import fs from "fs-extra"
|
|||
import sade from "sade"
|
||||
|
||||
import type {
|
||||
JorfArticle,
|
||||
JorfTextelr,
|
||||
JorfTexteVersion,
|
||||
} from "$lib/legal/jorf"
|
||||
import type {
|
||||
LegiArticle,
|
||||
LegiSectionTa,
|
||||
LegiSectionTaLienArt,
|
||||
LegiSectionTaLienSectionTa,
|
||||
LegiSectionTaStructure,
|
||||
LegiTextelr,
|
||||
LegiTexteVersion,
|
||||
} from "$lib/legal/legi"
|
||||
import type { ArticleLienDb, TexteVersionLienDb } from "$lib/legal/shared"
|
||||
import { db } from "$lib/server/databases"
|
||||
|
||||
interface Context {
|
||||
articleById: Record<string, JorfArticle | LegiArticle>
|
||||
causeIdById: Record<string, string>
|
||||
legiTexteInternalIds: Set<string>
|
||||
sectionTaById: Record<string, LegiSectionTa>
|
||||
textelrById: Record<string, JorfTextelr | LegiTextelr>
|
||||
texteVersionIdByArticleId: Record<string, string>
|
||||
texteVersionById: Record<string, JorfTexteVersion | LegiTexteVersion>
|
||||
}
|
||||
|
||||
function addCause(context: Context, causedId: string, causeId: string) {
|
||||
// const existingCauseId = context.causeIdById[causedId]
|
||||
// if (existingCauseId !== undefined) {
|
||||
// assert.strictEqual(existingCauseId, causeId)
|
||||
// } else {
|
||||
context.causeIdById[causedId] = causeId
|
||||
// }
|
||||
}
|
||||
|
||||
async function exportLegiArticle(
|
||||
context: Context,
|
||||
legiTexteId: string,
|
||||
depth: number,
|
||||
lienArticle: LegiSectionTaLienArt,
|
||||
article: LegiArticle,
|
||||
): Promise<void> {
|
||||
console.log(
|
||||
`${lienArticle["@id"]} ${" ".repeat(depth)}Article ${lienArticle["@num"]} (${lienArticle["@debut"]} — ${lienArticle["@fin"] === "2999-01-01" ? "…" : lienArticle["@fin"]}, ${lienArticle["@etat"]})`,
|
||||
)
|
||||
|
||||
let startCauseFound = false
|
||||
|
||||
for (const articleLien of await db<ArticleLienDb[]>`
|
||||
SELECT * FROM article_lien WHERE id = ${lienArticle["@id"]}
|
||||
`) {
|
||||
assert.strictEqual(articleLien.cidtexte, legiTexteId)
|
||||
if (articleLien.article_id in context.legiTexteInternalIds) {
|
||||
// Ignore internal links because a LEGI texte can't modify itself.
|
||||
continue
|
||||
}
|
||||
|
||||
console.log(
|
||||
`${" ".repeat(20)} ${" ".repeat(depth + 1)}${articleLien.article_id} cible: ${articleLien.cible} typelien: ${articleLien.typelien}`,
|
||||
)
|
||||
if (
|
||||
(articleLien.typelien === "ABROGATION" && articleLien.cible) ||
|
||||
(articleLien.typelien === "ABROGE" && !articleLien.cible)
|
||||
) {
|
||||
addCause(context, articleLien.id, articleLien.article_id)
|
||||
// stopCauseFound = true
|
||||
} else if (
|
||||
articleLien.typelien === "CITATION" ||
|
||||
(articleLien.typelien === "CODIFICATION" && articleLien.cible) ||
|
||||
(articleLien.typelien === "SPEC_APPLI" && articleLien.cible) ||
|
||||
(articleLien.typelien === "PILOTE_SUIVEUR" && !articleLien.cible) ||
|
||||
(articleLien.typelien === "TXT_ASSOCIE" && !articleLien.cible) ||
|
||||
articleLien.typelien === "TXT_SOURCE"
|
||||
) {
|
||||
// Ignore link.
|
||||
} else if (
|
||||
(articleLien.typelien === "CONCORDANCE" &&
|
||||
!articleLien.cible) /* le sens est étrange ? Ignorer ce lien ? */ ||
|
||||
(articleLien.typelien === "CONCORDANCE" && articleLien.cible) ||
|
||||
(articleLien.typelien === "CONCORDE" && !articleLien.cible)
|
||||
) {
|
||||
// L'article est créé par le déplacement d'un article existant dans une loi.
|
||||
addCause(context, articleLien.id, articleLien.article_id)
|
||||
startCauseFound = true
|
||||
} else if (articleLien.typelien === "CONCORDE" && articleLien.cible) {
|
||||
// L'article est déplacé ailleurs.
|
||||
addCause(context, articleLien.id, articleLien.article_id)
|
||||
// stopCauseFound = true
|
||||
} else if (
|
||||
(articleLien.typelien === "CREATION" && articleLien.cible) ||
|
||||
(articleLien.typelien === "CREE" && !articleLien.cible) ||
|
||||
(articleLien.typelien === "MODIFICATION" && articleLien.cible) ||
|
||||
(articleLien.typelien === "MODIFIE" && !articleLien.cible)
|
||||
) {
|
||||
addCause(context, articleLien.id, articleLien.article_id)
|
||||
startCauseFound = true
|
||||
} else if (articleLien.typelien === "CREATION" && !articleLien.cible) {
|
||||
// Quand un article est TRANSFERE cela s'accompation de la
|
||||
// CREATION d'un autre article.
|
||||
addCause(context, articleLien.id, articleLien.article_id)
|
||||
// stopCauseFound = true
|
||||
} else if (
|
||||
(articleLien.typelien === "DEPLACE" && !articleLien.cible) ||
|
||||
(articleLien.typelien === "DEPLACEMENT" && articleLien.cible)
|
||||
) {
|
||||
// L'article est créé par le déplacement d'un article existant.
|
||||
addCause(context, articleLien.id, articleLien.article_id)
|
||||
startCauseFound = true
|
||||
} else if (articleLien.typelien === "TRANSFERE" && !articleLien.cible) {
|
||||
// L'article est transféré ailleurs.
|
||||
addCause(context, articleLien.id, articleLien.article_id)
|
||||
// stopCauseFound = true
|
||||
} else if (articleLien.typelien === "TRANSFERT" && articleLien.cible) {
|
||||
// L'article provient d'un transfert.
|
||||
addCause(context, articleLien.id, articleLien.article_id)
|
||||
startCauseFound = true
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unexpected article_lien to article ${lienArticle["@id"]}: typelien=${articleLien.typelien}, cible=${articleLien.cible}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for (const texteVersionLien of await db<TexteVersionLienDb[]>`
|
||||
SELECT * FROM texte_version_lien WHERE id = ${lienArticle["@id"]}
|
||||
`) {
|
||||
assert.strictEqual(texteVersionLien.cidtexte, legiTexteId)
|
||||
if (texteVersionLien.texte_version_id in context.legiTexteInternalIds) {
|
||||
// Ignore internal links because a LEGI texte can't modify itself.
|
||||
continue
|
||||
}
|
||||
|
||||
console.log(
|
||||
`${" ".repeat(20)} ${" ".repeat(depth + 1)}${texteVersionLien.texte_version_id} cible: ${texteVersionLien.cible} typelien: ${texteVersionLien.typelien}`,
|
||||
)
|
||||
if (
|
||||
(texteVersionLien.typelien === "ABROGATION" && texteVersionLien.cible) ||
|
||||
(texteVersionLien.typelien === "ANNULATION" && texteVersionLien.cible)
|
||||
) {
|
||||
addCause(context, texteVersionLien.id, texteVersionLien.texte_version_id)
|
||||
// stopCauseFound = true
|
||||
} else if (
|
||||
texteVersionLien.typelien === "CITATION" ||
|
||||
(texteVersionLien.typelien === "CODIFICATION" &&
|
||||
texteVersionLien.cible) ||
|
||||
(texteVersionLien.typelien === "SPEC_APPLI" && texteVersionLien.cible) ||
|
||||
(texteVersionLien.typelien === "TXT_SOURCE" && !texteVersionLien.cible)
|
||||
) {
|
||||
// Ignore link.
|
||||
} else if (
|
||||
texteVersionLien.typelien === "CONCORDANCE" &&
|
||||
texteVersionLien.cible
|
||||
) {
|
||||
// L'article est créé par le déplacement d'un article existant dans une loi.
|
||||
addCause(context, texteVersionLien.id, texteVersionLien.texte_version_id)
|
||||
startCauseFound = true
|
||||
} else if (
|
||||
(texteVersionLien.typelien === "CREATION" && texteVersionLien.cible) ||
|
||||
// || (texteVersionLien.typelien === "CREE" && !texteVersionLien.cible)
|
||||
(texteVersionLien.typelien === "MODIFICATION" &&
|
||||
texteVersionLien.cible) ||
|
||||
// || (texteVersionLien.typelien === "MODIFIE" && !texteVersionLien.cible)
|
||||
(texteVersionLien.typelien === "RECTIFICATION" && texteVersionLien.cible)
|
||||
) {
|
||||
addCause(context, texteVersionLien.id, texteVersionLien.texte_version_id)
|
||||
startCauseFound = true
|
||||
} else if (
|
||||
texteVersionLien.typelien === "TRANSFERT" &&
|
||||
texteVersionLien.cible
|
||||
) {
|
||||
// L'article provient d'un transfert.
|
||||
addCause(context, texteVersionLien.id, texteVersionLien.texte_version_id)
|
||||
startCauseFound = true
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unexpected texte_version_lien to article ${lienArticle["@id"]}: typelien=${texteVersionLien.typelien}, cible=${texteVersionLien.cible}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const articleLiens = article.LIENS?.LIEN
|
||||
if (articleLiens !== undefined) {
|
||||
for (const articleLien of articleLiens) {
|
||||
if (articleLien["@id"]! in context.legiTexteInternalIds) {
|
||||
// Ignore internal links because a LEGI texte can't modify itself.
|
||||
continue
|
||||
}
|
||||
|
||||
console.log(
|
||||
`${" ".repeat(20)} ${" ".repeat(depth + 1)}sens: ${articleLien["@sens"]} typelien: ${articleLien["@typelien"]} ${articleLien["@cidtexte"]} ${articleLien["@id"]}${articleLien["@nortexte"] === undefined ? "" : ` ${articleLien["@nortexte"]}`}${articleLien["@num"] === undefined ? "" : ` ${articleLien["@num"]}`} ${articleLien["@naturetexte"]} du ${articleLien["@datesignatexte"]} : ${articleLien["#text"]}`,
|
||||
)
|
||||
if (
|
||||
(articleLien["@typelien"] === "ABROGATION" &&
|
||||
articleLien["@sens"] === "source") ||
|
||||
(articleLien["@typelien"] === "ABROGE" &&
|
||||
articleLien["@sens"] === "cible") ||
|
||||
(articleLien["@typelien"] === "ANNULATION" &&
|
||||
articleLien["@sens"] === "source")
|
||||
) {
|
||||
addCause(context, article.META.META_COMMUN.ID, articleLien["@id"]!)
|
||||
// stopCauseFound = true
|
||||
} else if (
|
||||
articleLien["@typelien"] === "CITATION" ||
|
||||
(articleLien["@typelien"] === "CODIFICATION" &&
|
||||
articleLien["@sens"] === "source") ||
|
||||
(articleLien["@typelien"] === "HISTO" &&
|
||||
articleLien["@sens"] ===
|
||||
"source") /* Au moins sur un exemple, le lien est vide à part ces 2 champs */ ||
|
||||
(articleLien["@typelien"] === "PILOTE_SUIVEUR" &&
|
||||
articleLien["@sens"] === "cible") ||
|
||||
(articleLien["@typelien"] === "SPEC_APPLI" &&
|
||||
articleLien["@sens"] === "source") ||
|
||||
(articleLien["@typelien"] === "TXT_ASSOCIE" &&
|
||||
articleLien["@sens"] === "cible") ||
|
||||
articleLien["@typelien"] === "TXT_SOURCE"
|
||||
) {
|
||||
// Ignore link.
|
||||
} else if (
|
||||
(articleLien["@typelien"] === "CONCORDANCE" &&
|
||||
articleLien["@sens"] ===
|
||||
"cible") /* Le sens est étrange ? Ignorer ce lien ? */ ||
|
||||
(articleLien["@typelien"] === "CONCORDANCE" &&
|
||||
articleLien["@sens"] === "source") ||
|
||||
(articleLien["@typelien"] === "CONCORDE" &&
|
||||
articleLien["@sens"] === "cible")
|
||||
) {
|
||||
// L'article est créé par le déplacement d'un article existant dans une loi.
|
||||
addCause(context, article.META.META_COMMUN.ID, articleLien["@id"]!)
|
||||
startCauseFound = true
|
||||
} else if (
|
||||
articleLien["@typelien"] === "CONCORDE" &&
|
||||
articleLien["@sens"] === "source"
|
||||
) {
|
||||
// L'article est déplacé aiileurs.
|
||||
addCause(context, article.META.META_COMMUN.ID, articleLien["@id"]!)
|
||||
// stopCauseFound = true
|
||||
} else if (
|
||||
(articleLien["@typelien"] === "CREATION" &&
|
||||
articleLien["@sens"] === "source") ||
|
||||
(articleLien["@typelien"] === "CREE" &&
|
||||
articleLien["@sens"] === "cible") ||
|
||||
(articleLien["@typelien"] === "MODIFICATION" &&
|
||||
articleLien["@sens"] === "source") ||
|
||||
(articleLien["@typelien"] === "MODIFIE" &&
|
||||
articleLien["@sens"] === "cible")
|
||||
) {
|
||||
addCause(context, article.META.META_COMMUN.ID, articleLien["@id"]!)
|
||||
startCauseFound = true
|
||||
} else if (
|
||||
articleLien["@typelien"] === "CREATION" &&
|
||||
articleLien["@sens"] === "cible"
|
||||
) {
|
||||
// Quand un article est TRANSFERE cela s'accompation de la
|
||||
// CREATION d'un autre article.
|
||||
addCause(context, article.META.META_COMMUN.ID, articleLien["@id"]!)
|
||||
// stopCauseFound = true
|
||||
} else if (
|
||||
(articleLien["@typelien"] === "DEPLACE" &&
|
||||
articleLien["@sens"] === "cible") ||
|
||||
(articleLien["@typelien"] === "DEPLACEMENT" &&
|
||||
articleLien["@sens"] === "source")
|
||||
) {
|
||||
// L'article est créé par le déplacement d'un article existant.
|
||||
addCause(context, article.META.META_COMMUN.ID, articleLien["@id"]!)
|
||||
startCauseFound = true
|
||||
} else if (
|
||||
articleLien["@typelien"] === "RECTIFICATION" &&
|
||||
articleLien["@sens"] === "source"
|
||||
) {
|
||||
addCause(context, article.META.META_COMMUN.ID, articleLien["@id"]!)
|
||||
startCauseFound = true
|
||||
} else if (
|
||||
articleLien["@typelien"] === "TRANSFERE" &&
|
||||
articleLien["@sens"] === "cible"
|
||||
) {
|
||||
// L'article est transféré ailleurs.
|
||||
addCause(context, article.META.META_COMMUN.ID, articleLien["@id"]!)
|
||||
// stopCauseFound = true
|
||||
} else if (
|
||||
articleLien["@typelien"] === "TRANSFERT" &&
|
||||
articleLien["@sens"] === "source"
|
||||
) {
|
||||
// L'article provient d'un transfert.
|
||||
addCause(context, article.META.META_COMMUN.ID, articleLien["@id"]!)
|
||||
startCauseFound = true
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unexpected LIEN in article ${article.META.META_COMMUN.ID}: @typelien=${articleLien["@typelien"]}, @sens=${articleLien["@sens"]}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function exportLegiTexteToMarkdown(
|
||||
legiTexteId: string,
|
||||
targetDir: string,
|
||||
): Promise<void> {
|
||||
const context: Context = {
|
||||
articleById: {},
|
||||
causeIdById: {},
|
||||
legiTexteInternalIds: new Set(),
|
||||
sectionTaById: {},
|
||||
textelrById: {},
|
||||
texteVersionIdByArticleId: {},
|
||||
texteVersionById: {},
|
||||
}
|
||||
context.legiTexteInternalIds.add(legiTexteId)
|
||||
|
||||
const texteVersion = (
|
||||
await db<{ data: LegiTexteVersion }[]>`
|
||||
SELECT data FROM texte_version WHERE id = ${legiTexteId}
|
||||
`
|
||||
)[0]?.data
|
||||
assert.notStrictEqual(texteVersion, undefined)
|
||||
context.texteVersionById[legiTexteId] = texteVersion
|
||||
|
||||
const textelr = (
|
||||
await db<{ data: LegiTextelr }[]>`
|
||||
|
@ -28,6 +326,7 @@ async function exportLegiTexteToMarkdown(
|
|||
`
|
||||
)[0]?.data
|
||||
assert.notStrictEqual(textelr, undefined)
|
||||
context.textelrById[legiTexteId] = textelr
|
||||
|
||||
const meta = texteVersion.META
|
||||
const metaTexteVersion = meta.META_SPEC.META_TEXTE_VERSION
|
||||
|
@ -35,19 +334,87 @@ async function exportLegiTexteToMarkdown(
|
|||
`${meta.META_COMMUN.ID} ${metaTexteVersion.TITREFULL ?? metaTexteVersion.TITRE ?? meta.META_COMMUN.ID} (${metaTexteVersion.DATE_DEBUT ?? ""} — ${metaTexteVersion.DATE_FIN === "2999-01-01" ? "…" : (metaTexteVersion.DATE_FIN ?? "")}, ${metaTexteVersion.ETAT})`,
|
||||
)
|
||||
|
||||
// First Pass: Register IDs of internal objects
|
||||
|
||||
const textelrStructure = textelr.STRUCT
|
||||
const liensArticles = textelrStructure?.LIEN_ART
|
||||
if (liensArticles !== undefined) {
|
||||
for (const lienArticle of liensArticles) {
|
||||
context.legiTexteInternalIds.add(lienArticle["@id"])
|
||||
}
|
||||
}
|
||||
|
||||
for await (const { sectionTa } of walkStructureTree(
|
||||
context,
|
||||
textelrStructure as LegiSectionTaStructure,
|
||||
)) {
|
||||
const liensArticles = sectionTa?.STRUCTURE_TA?.LIEN_ART
|
||||
if (liensArticles !== undefined) {
|
||||
for (const lienArticle of liensArticles) {
|
||||
context.legiTexteInternalIds.add(lienArticle["@id"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second Pass
|
||||
|
||||
if (liensArticles !== undefined) {
|
||||
for (const lienArticle of liensArticles) {
|
||||
const article = (await getOrLoadArticle(
|
||||
context,
|
||||
lienArticle["@id"],
|
||||
)) as LegiArticle
|
||||
await exportLegiArticle(context, legiTexteId, 0, lienArticle, article)
|
||||
}
|
||||
}
|
||||
|
||||
for await (const {
|
||||
lienSectionTa,
|
||||
parentsSectionTa,
|
||||
sectionTa,
|
||||
} of walkStructureTree(textelrStructure as LegiSectionTaStructure)) {
|
||||
} of walkStructureTree(context, textelrStructure as LegiSectionTaStructure)) {
|
||||
console.log(
|
||||
`${sectionTa.ID} ${" ".repeat(parentsSectionTa.length + 1)}${sectionTa.TITRE_TA?.replace(/\s+/g, " ") ?? sectionTa.ID} (${lienSectionTa["@debut"]} — ${lienSectionTa["@fin"] === "2999-01-01" ? "…" : lienSectionTa["@fin"]}, ${lienSectionTa["@etat"]})`,
|
||||
)
|
||||
|
||||
for (const articleLien of await db<ArticleLienDb[]>`
|
||||
SELECT * FROM article_lien WHERE id = ${lienSectionTa["@id"]}
|
||||
`) {
|
||||
assert.strictEqual(articleLien.cidtexte, legiTexteId)
|
||||
console.log(
|
||||
`${" ".repeat(20)} ${" ".repeat(parentsSectionTa.length + 1)} ${articleLien.article_id} cible: ${articleLien.cible} typelien: ${articleLien.typelien}`,
|
||||
)
|
||||
}
|
||||
for (const texteVersionLien of await db<TexteVersionLienDb[]>`
|
||||
SELECT * FROM texte_version_lien WHERE id = ${lienSectionTa["@id"]}
|
||||
`) {
|
||||
assert.strictEqual(texteVersionLien.cidtexte, legiTexteId)
|
||||
console.log(
|
||||
`${" ".repeat(20)} ${" ".repeat(parentsSectionTa.length + 1)} ${texteVersionLien.texte_version_id} cible: ${texteVersionLien.cible} typelien: ${texteVersionLien.typelien}`,
|
||||
)
|
||||
}
|
||||
|
||||
const liensArticles = sectionTa?.STRUCTURE_TA?.LIEN_ART
|
||||
if (liensArticles !== undefined) {
|
||||
for (const lienArticle of liensArticles) {
|
||||
const article = (await getOrLoadArticle(
|
||||
context,
|
||||
lienArticle["@id"],
|
||||
)) as LegiArticle
|
||||
await exportLegiArticle(
|
||||
context,
|
||||
legiTexteId,
|
||||
parentsSectionTa.length + 2,
|
||||
lienArticle,
|
||||
article,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function* walkStructureTree(
|
||||
context: Context,
|
||||
structure: LegiSectionTaStructure,
|
||||
parentsSectionTa: LegiSectionTa[] = [],
|
||||
): AsyncGenerator<
|
||||
|
@ -61,16 +428,21 @@ async function* walkStructureTree(
|
|||
const liensSectionTa = structure?.LIEN_SECTION_TA
|
||||
if (liensSectionTa !== undefined) {
|
||||
for (const lienSectionTa of liensSectionTa) {
|
||||
const childSectionTa = (
|
||||
await db<{ data: LegiSectionTa }[]>`
|
||||
SELECT data FROM section_ta WHERE id = ${lienSectionTa["@id"]}
|
||||
`
|
||||
)[0]?.data
|
||||
assert.notStrictEqual(childSectionTa, undefined)
|
||||
let childSectionTa = context.sectionTaById[lienSectionTa["@id"]]
|
||||
if (childSectionTa === undefined) {
|
||||
childSectionTa = (
|
||||
await db<{ data: LegiSectionTa }[]>`
|
||||
SELECT data FROM section_ta WHERE id = ${lienSectionTa["@id"]}
|
||||
`
|
||||
)[0]?.data
|
||||
assert.notStrictEqual(childSectionTa, undefined)
|
||||
context.sectionTaById[lienSectionTa["@id"]] = childSectionTa
|
||||
context.legiTexteInternalIds.add(lienSectionTa["@id"])
|
||||
}
|
||||
yield { lienSectionTa, parentsSectionTa, sectionTa: childSectionTa }
|
||||
const childStructure = childSectionTa.STRUCTURE_TA
|
||||
if (childStructure !== undefined) {
|
||||
yield* walkStructureTree(childStructure, [
|
||||
yield* walkStructureTree(context, childStructure, [
|
||||
...parentsSectionTa,
|
||||
childSectionTa,
|
||||
])
|
||||
|
@ -79,6 +451,23 @@ async function* walkStructureTree(
|
|||
}
|
||||
}
|
||||
|
||||
async function getOrLoadArticle(
|
||||
context: Context,
|
||||
articleId: string,
|
||||
): Promise<JorfArticle | LegiArticle> {
|
||||
let article = context.articleById[articleId]
|
||||
if (article === undefined) {
|
||||
article = (
|
||||
await db<{ data: LegiArticle }[]>`
|
||||
SELECT data FROM article WHERE id = ${articleId}
|
||||
`
|
||||
)[0]?.data
|
||||
assert.notStrictEqual(article, undefined)
|
||||
context.articleById[articleId] = article
|
||||
}
|
||||
return article
|
||||
}
|
||||
|
||||
sade("export_legi_texte_to_markdown <legiTexteId> <targetDir>", true)
|
||||
.describe(
|
||||
"Convert a LEGI texte (code, law, etc) to a markdown tree in a directory",
|
||||
|
|
Loading…
Reference in a new issue