Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,22 @@

> Short code snippets for all your development needs

* Visit [the website](https://30secondsofcode.org) to view our snippet collection.
* Search for snippets that suit your needs. You can search by name, tag, language or using a snippet's description. Just start typing a term and see what comes up.
* Visit [the website](https://30secondsofcode.org) to view the snippet collection.
* Search for snippets and collections that suit your needs, using the name, tags, language or description.
* Browse all [snippets](https://30secondsofcode.org/snippets/p/1) or individual [snippet collections](https://30secondsofcode.org/collections/p/1) for each topic.
* Click on each snippet card to view the whole snippet, including code, explanation and examples.
* You can copy code blocks on any snippet card, using the copy button at the top right.
* If you like the project, give it a star. It means a lot to the people maintaining it.
* If you like the project, give it a star. It means a lot.

## Want to contribute?

* If you want to help us improve, take a minute to read the [Contribution Guidelines](/CONTRIBUTING.md) first.
* Use the relevant `template.md` to add new snippets to each collection.
* If you find a problem with a specific snippet, please [open an issue](https://github.com/30-seconds/30-seconds-of-code/issues/new).
* If you find a problem with the website, please [open an issue](https://github.com/30-seconds/30-seconds-of-code/issues/new).
* If you want to help, take a minute to read the [Contribution Guidelines](/CONTRIBUTING.md) first.
* If you want to discuss a new snippet idea, first you should [create a discussion](https://github.com/Chalarangelo/30-seconds-of-code/discussions/new?category=ideas).
* If you find a problem with a specific snippet or the website, please [open an issue](https://github.com/30-seconds/30-seconds-of-code/issues/new).

## Credits

* This repository is maintained by [Angelos Chalaris]([https://github.com/30-seconds](https://github.com/Chalarangelo)).
* All snippets are licensed under the CC-BY-4.0 License, unless explicitly stated otherwise.
* All snippets are licensed under the [CC-BY-4.0 License](https://creativecommons.org/licenses/by/4.0/), unless explicitly stated otherwise.
* Logos, names and trademarks are not to be used without the explicit consent of the owner.
* The website is powered by [Netlify](https://www.netlify.com/), [Astro](https://astro.build/) & [GitHub](https://github.com/).
Binary file modified assets/icons.woff2
Binary file not shown.
1 change: 1 addition & 0 deletions content/languages/css.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
short: css
long: css
name: CSS
additionalReferences: [js, html]
1 change: 1 addition & 0 deletions content/languages/html.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
short: html
long: html
name: HTML
additionalReferences: [css, js]
1 change: 1 addition & 0 deletions content/languages/javascript.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
short: js
long: javascript
name: JavaScript
additionalReferences: [css, html]
references:
...: >-
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
Expand Down
1 change: 1 addition & 0 deletions content/languages/react.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
short: jsx
long: react
name: React
additionalReferences: [css, html, js]
references:
useCallback(): https://react.dev/reference/react/useCallback
useContext(): https://react.dev/reference/react/useContext
Expand Down
69 changes: 10 additions & 59 deletions src/blocks/extractor/extractor.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* eslint-disable no-unused-vars */
import pathSettings from '#settings/paths';
import JSX_SNIPPET_PRESETS from '#settings/jsxSnippetPresets';
import { Logger } from '#blocks/utilities/logger';
import { TextParser } from '#blocks/extractor/textParser';
import { MarkdownParser } from '#blocks/extractor/markdownParser';
Expand All @@ -9,10 +8,6 @@ import { YAMLHandler } from '#blocks/utilities/yamlHandler';
import { stripMarkdownFormat } from '#utils';

const mdCodeFence = '```';
const codeMatcher = new RegExp(
`${mdCodeFence}.*\r?\n(?<code>[\\S\\s]*?)${mdCodeFence}`,
'g'
);

const { rawContentPath: contentDir } = pathSettings;

Expand Down Expand Up @@ -89,20 +84,27 @@ export class Extractor {
const languageData = YAMLHandler.fromGlob(
`${contentDir}/languages/*.yaml`
).reduce((acc, language) => {
const { short, long, name, references = {} } = language;
const {
short,
long,
name,
references = {},
additionalReferences = [],
} = language;
acc.set(long, {
id: long,
long,
short,
name,
references: new Map(Object.entries(references)),
allLanguageReferences: [short, ...additionalReferences],
});
return acc;
}, new Map());
logger.success('Finished extracting language data');
Extractor.languageData = languageData;
Extractor.data.languages = [...languageData].map(([id, data]) => {
const { references, ...restData } = data;
const { references, allLanguageReferences, ...restData } = data;
return { ...restData };
});
MarkdownParser.loadLanguageData([...Extractor.languageData.values()]);
Expand Down Expand Up @@ -193,20 +195,7 @@ export class Extractor {
unlisted,
} = snippet;

// This check might be overkill, but better safe than sorry
const isBlog = type !== 'snippet' && filePath.includes('/articles/');
const isCSS = filePath.includes('/css/') && type === 'snippet';
const isReact = filePath.includes('/react/') && type === 'snippet';

const language = Extractor.languageData.get(languageKey) || undefined;
const languageKeys = isBlog
? []
: isCSS
? ['js', 'html', 'css']
: isReact
? ['js', 'jsx']
: [language];

const id = filePath.replace(`${contentDir}/snippets/`, '').slice(0, -3);
const tags = rawTags.map(tag => tag.toLowerCase());

Expand All @@ -225,49 +214,12 @@ export class Extractor {
logger.warn(`Snippet ${id} has a long SEO description.`);
}

let code = null;

if (isCSS || isReact) {
const codeBlocks = [...body.matchAll(codeMatcher)].map(v =>
v.groups.code.trim()
);

if (isCSS) {
code = {
html: codeBlocks[0],
css: codeBlocks[1],
js: codeBlocks[2] || '',
};
}

if (isReact) {
code =
codeBlocks.length > 2
? {
js: `${codeBlocks[1]}\n\n${codeBlocks[2]}`,
css: codeBlocks[0],
}
: {
js: `${codeBlocks[0]}\n\n${codeBlocks[1]}`,
css: '',
};
/* eslint-disable camelcase */
code = {
...code,
html: JSX_SNIPPET_PRESETS.envHtml,
js_pre_processor: JSX_SNIPPET_PRESETS.jsPreProcessor,
js_external: JSX_SNIPPET_PRESETS.jsImports.join(';'),
};
/* eslint-enable camelcase */
}
}

const html = MarkdownParser.parseSegments(
{
fullDescription: fullText,
description: shortText,
},
languageKeys
language ? language.allLanguageReferences : []
);

return {
Expand All @@ -282,7 +234,6 @@ export class Extractor {
shortText,
fullText,
...html,
code,
cover,
seoDescription,
language: languageKey,
Expand Down
4 changes: 0 additions & 4 deletions src/blocks/models/snippet.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export const snippet = {
fullText: 'string',
descriptionHtml: 'string',
fullDescriptionHtml: 'string',
code: 'object',
cover: 'string',
seoDescription: 'string',
},
Expand Down Expand Up @@ -73,9 +72,6 @@ export const snippet = {
body: snippet => convertToSeoSlug(snippet.fileName.slice(0, -3)),
cache: true,
},
url: snippet =>
`https://github.com/Chalarangelo/30-seconds-of-code/blob/master/content/snippets${snippet.slug}.md`,
actionType: snippet => (snippet.code ? 'codepen' : undefined),
isScheduled: {
body: snippet => snippet.dateModified > new Date(),
cache: true,
Expand Down
3 changes: 0 additions & 3 deletions src/blocks/serializers/snippetContextSerializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,9 @@ export const snippetContextSerializer = {
attributes: [
'title',
'fullDescription',
'url',
'slug',
['dateFormatted', 'date'],
['formattedTags', 'tags'],
'actionType',
'code',
['coverUrl', 'cover'],
],
};
31 changes: 31 additions & 0 deletions src/blocks/utilities/preparedQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ const coverAssetPath = 'content/assets/cover';
// Redirects constants
const redirectsPath = 'content/redirects.yaml';

// Collection config constants
const contentConfigsGlob = 'content/collections/**/*.yaml';

// Performance constants (manually import Pages.csv from Google Search Console)
const performancePath = 'imported/Pages.csv';

Expand Down Expand Up @@ -307,4 +310,32 @@ export class PreparedQueries {
heading: headingMatcher.test(fullText),
};
});

/**
* Returns an object with information about references with the same code
* identifier in multiple languages.
* @param {string[]} languageKeys - The language keys to get references for.
*/
static duplicateReferences =
() =>
(languageKeys = ['js', 'css', 'html', 'jsx']) =>
withCache(`duplicateReferences#${languageKeys.join(',')}`, () => {
const referenceMap = new Map();
YAMLHandler.fromGlob(contentConfigsGlob).forEach(config => {
const { short, references } = config;
if (!references || !references.length) return;
references.forEach(reference => {
const referenceLanguages = referenceMap.get(reference) || [];
referenceLanguages.push(short);
referenceMap.set(reference, referenceLanguages);
});
});
return [...referenceMap.entries()].reduce(
(acc, [reference, languages]) => {
if (languages.length > 1) acc[reference] = languages;
return acc;
},
{}
);
});
}
15 changes: 0 additions & 15 deletions src/components/SnippetCard.astro
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,5 @@ const { snippet } = Astro.props;
class='card-description flex flex-col'
set:html={snippet.fullDescription}
/>
<div class='card-actions flex j-space-evenly mt-4 mx-0 mb-0'>
{
snippet.actionType === 'codepen' && (
<button
class={`${prefabs.button} ${prefabs.actionButton} ${prefabs.iconButton} flex-none before:fs-md icon-codepen my-0 mx-2 p-3`}
title='View CodePen'
/>
)
}
<a
class={`${prefabs.button} ${prefabs.actionButton} ${prefabs.iconButton} flex-none before:fs-md icon-github my-0 mx-2 p-3`}
href={snippet.url}
rel='nofollow noopener noreferrer'
target='_blank'
title='View on GitHub'></a>
</div>
</article>
60 changes: 0 additions & 60 deletions src/components/SnippetScripts.astro

This file was deleted.

7 changes: 0 additions & 7 deletions src/icons/codepen.svg

This file was deleted.

8 changes: 0 additions & 8 deletions src/icons/github.svg

This file was deleted.

Loading