-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Notion Importer #7
base: main
Are you sure you want to change the base?
Conversation
theianjones
commented
Oct 11, 2022
src/converters/notion/index.ts
Outdated
const isPageLink = (s: string) => { | ||
return !!s.match(/\.md$/) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if theres {some string}.md
then we know notion made a link
src/converters/notion/index.ts
Outdated
const parseFileContents = (fileContent: string): Record<string, string>[] => { | ||
// Looks like notion exporter inserts a Zero Width No-Break Space character at the start of the file. | ||
return parse(fileContent.slice(1), {columns: true, skip_empty_lines: true, encoding: 'utf8', }) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using csv-parse
to extract the contents. Im not married to this package if theres a better one out there.
src/converters/notion/index.ts
Outdated
const createRootNode = (parsedContent: Record<string, string>): TanaIntermediateNode => { | ||
const newNode = { | ||
uid: idgenerator(), | ||
name: parsedContent['Name'], | ||
createdAt: new Date().getTime(), | ||
editedAt: new Date().getTime(), | ||
type: 'node', | ||
todoState: undefined, | ||
} as const | ||
|
||
const children = createChildrenForRoot(parsedContent) | ||
return {...newNode, children} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this creates a root node for each of the objects.
We statically use the Name
column to determine the node.name
of the root node.
Afaict, every notion table has a Name
column. If not, this will need to be a param passed in from the CLI.
src/converters/notion/index.ts
Outdated
const createChildrenForRoot = (parsedContent: Record<string, string>): TanaIntermediateNode[] | undefined => { | ||
const keys = Object.keys(parsedContent) | ||
const fields = keys.map(key => { | ||
const newField = { | ||
uid: idgenerator(), | ||
name: key, | ||
createdAt: new Date().getTime(), | ||
editedAt: new Date().getTime(), | ||
type: 'field', | ||
todoState: undefined, | ||
} | ||
|
||
const fieldChildren = [{ | ||
uid: idgenerator(), | ||
name: parsedContent[key], | ||
createdAt: new Date().getTime(), | ||
editedAt: new Date().getTime(), | ||
type: 'node', | ||
todoState: undefined, | ||
}] | ||
|
||
return {...newField, children: fieldChildren} | ||
}) as TanaIntermediateNode[] | ||
|
||
return fields | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the fields are created for each key found in the parsedContent
. For this pokemon notion a parsedContent
object would look like this:
https://gist.github.com/theianjones/82188ec7fba89ead74ed85470c2e5d16
src/converters/notion/index.ts
Outdated
const createSummary = (nodes: TanaIntermediateNode[]) => { | ||
return { | ||
leafNodes: countLeafNodes(nodes), | ||
calendarNodes: 0, | ||
fields: countFields(nodes), | ||
brokenRefs: countBrokenLinks(nodes), | ||
topLevelNodes: nodes.length, | ||
totalNodes: countTotalNodes(nodes) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could definitely be more efficient (one reduce function) but I took the straight forward approach.
src/converters/notion/index.ts
Outdated
const mergeAttributes = (a: AttributesMap, b: AttributesMap): AttributesMap => { | ||
return Object.values(b).reduce((acc, curr) => { | ||
const old = acc[curr.name] | ||
|
||
if(!old){ | ||
return { | ||
...acc, | ||
[curr.name]: curr | ||
} | ||
} | ||
|
||
|
||
const newPatch = { | ||
count: old.count + curr.count, | ||
values: [...new Set(old.values.concat(curr.values))], | ||
} | ||
|
||
return { | ||
...acc, | ||
[curr.name]: { | ||
...old, | ||
...newPatch | ||
} | ||
} | ||
}, a) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to deep merge attributes or only 1 will show up
src/converters/notion/index.ts
Outdated
const fixPageLinks = (nodeMap: Record<TanaIntermediateAttribute['name'], TanaIntermediateNode>, nodes: TanaIntermediateNode[]): TanaIntermediateNode[] => { | ||
return nodes.map((node) => { | ||
const refs: string[] = [] | ||
const newName = node.name.split(',').map((potentialLink) => { | ||
if(!isPageLink(potentialLink)){ | ||
return potentialLink | ||
} | ||
|
||
const linkName = getNameOutOfLink(potentialLink) | ||
|
||
if(!linkName || !nodeMap[linkName]){ | ||
return potentialLink | ||
} | ||
|
||
const refFromLink = nodeMap[linkName].uid | ||
|
||
refs.push(refFromLink) | ||
return `[[${refFromLink}]]` | ||
}).join(',') | ||
|
||
const newChildren = node.children ? fixPageLinks(nodeMap, node.children) : undefined | ||
|
||
return { | ||
...node, | ||
name: newName, | ||
refs, | ||
children: newChildren | ||
} | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After nodes are created, we can go through an find an links that can be replaced with node references.